diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst index 8a44db79a07ff..ac87afb9a37bf 100644 --- a/clang/docs/InternalsManual.rst +++ b/clang/docs/InternalsManual.rst @@ -427,6 +427,17 @@ Description: This is useful when the argument could be a string in some cases, but another class in other cases, and it needs to be quoted consistently. +**"unquoted" format** + +Example: + ``"conflicting attributes were '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}0(%unquoted1)' and '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}2(%unquoted3)'"`` +Class: + ``Expr, Decl`` +Description: + This is a simple formatter which omits the single quotes from a class + that would normally be printed quoted. This is useful when the diagnostic + uses the argument to construct a larger type or expression. + .. _internals-producing-diag: Producing the Diagnostic diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index 0b1fcec3bb286..2a413ff4e9fb3 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -354,6 +354,22 @@ class ParamIdx { assertComparable(I); return Idx == I.Idx; } + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Unlike operator== which requires isValid to be true for both objects, + /// this relaxes that constraint and returns true when comparing two + /// invalid objects. An invalid object does not equal any valid object. + bool equals(const ParamIdx &I) const { + assert(HasThis == I.HasThis && + "ParamIdx must be for the same function to be compared"); + if (isValid() != I.isValid()) + return false; + if (!isValid()) { + assert(!I.isValid()); + return true; + } + return Idx == I.Idx; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ bool operator!=(const ParamIdx &I) const { assertComparable(I); return Idx != I.Idx; diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 1ebf67dab828c..47adfc9a6154d 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2605,6 +2605,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isBidiIndexablePointerType() const; bool isUnspecifiedPointerType() const; bool isSafePointerType() const; + bool isImplicitSafePointerType() const; /* TO_UPSTREAM(BoundsSafety) OFF */ bool isSignableType(const ASTContext &Ctx) const; bool isSignablePointerType() const; @@ -8839,6 +8840,19 @@ inline bool Type::isSafePointerType() const { isValueTerminatedType()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +inline bool Type::isImplicitSafePointerType() const { + if (auto AT = this->getAs()) { + if (AT->getAttrKind() == attr::PtrAutoAttr) { + assert(isSafePointerType()); + return true; + } + return AT->getEquivalentType()->isImplicitSafePointerType(); + } + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + inline bool Type::isPointerTypeWithBounds() const { const auto *PT = dyn_cast(CanonicalType); return PT && PT->getPointerAttributes().hasUpperBound(); diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 40f4937961761..830a8cc0e4f68 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3444,6 +3444,9 @@ def err_attribute_only_once_per_parameter : Error< "%0 attribute can only be applied once per parameter">; def err_mismatched_uuid : Error<"uuid does not match previous declaration">; def note_previous_uuid : Note<"previous uuid specified here">; +/* TO_UPSTREAM(BoundsSafety) ON */ +def err_mismatched_alloc_size : Error<"'alloc_size' attribute does not match previous declaration">; +/* TO_UPSTREAM(BoundsSafety) OFF */ def warn_attribute_pointers_only : Warning< "%0 attribute only applies to%select{| constant}1 pointer arguments">, InGroup; @@ -12808,7 +12811,19 @@ 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">; + "conflicting arguments for %select{end|terminator}0 were %1 and %2">; +def warn_bounds_safety_ignored_implicit_sized_by_or_null : Warning< + "implicit __sized_by_or_null attribute ignored because of explicit __sized_by">; +def note_bounds_safety_overriding_implicit_sized_by_or_null_silence : Note< + "add _Nonnull qualifier to return type to silence this warning">; +def note_bounds_safety_conflicting_count_attribute : Note< + "conflicting attributes were '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}0(%unquoted1)' and '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}2(%unquoted3)'">; +def note_bounds_safety_implicit_size_from_alloc_size_attr : Note< + "function return type implicitly __sized_by%select{|_or_null}0 because of the function's 'alloc_size' %select{and 'returns_nonnull' attributes|attribute}0">; +def err_invalid_return_type_for_alloc_size : Error< + "invalid return type %0 for function with alloc_size attribute; '__sized_by_or_null(%unquoted1)' or '__sized_by(%unquoted1)' required">; +def note_attribute_inherited : Note< + "attribute inherited from previous declaration here">; 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; diff --git a/clang/include/clang/Sema/Attr.h b/clang/include/clang/Sema/Attr.h index 3f0b10212789a..31756238283c0 100644 --- a/clang/include/clang/Sema/Attr.h +++ b/clang/include/clang/Sema/Attr.h @@ -80,6 +80,13 @@ inline const ParmVarDecl *getFunctionOrMethodParam(const Decl *D, return nullptr; } +/* TO_UPSTREAM(BoundsSafety) ON */ +inline ParmVarDecl *getFunctionOrMethodParam(Decl *D, unsigned Idx) { + return const_cast( + getFunctionOrMethodParam(const_cast(D), Idx)); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + inline QualType getFunctionOrMethodParamType(const Decl *D, unsigned Idx) { if (const FunctionType *FnTy = D->getFunctionType()) return cast(FnTy)->getParamType(Idx); diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h index 922a818a48aed..c9253566add7b 100644 --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -962,7 +962,10 @@ class ParsedAttributes : public ParsedAttributesView { void takeAllFrom(ParsedAttributes &Other) { assert(&Other != this && "ParsedAttributes can't take attributes from itself"); - addAll(Other.begin(), Other.end()); + /* TO_UPSTREAM(BoundsSafety) ON + * Upstream uses addAll() instead, but that changes the attribute order. */ + addAllAtEnd(Other.begin(), Other.end()); + /* TO_UPSTREAM(BoundsSafety) OFF */ Other.clearListOnly(); pool.takeAllFrom(Other.pool); } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0e88aba5caf27..0de3acf2fcd3e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5390,6 +5390,9 @@ class Sema final : public SemaBase { MinSizeAttr *mergeMinSizeAttr(Decl *D, const AttributeCommonInfo &CI); OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, const AttributeCommonInfo &CI); + /* TO_UPSTREAM(BoundsSafety) ON */ + AllocSizeAttr *mergeAllocSizeAttr(NamedDecl *D, const AllocSizeAttr &ASA); + /* TO_UPSTREAM(BoundsSafety) OFF */ InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const ParsedAttr &AL); InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const InternalLinkageAttr &AL); @@ -5485,6 +5488,12 @@ class Sema final : public SemaBase { void DiagnoseUnknownAttribute(const ParsedAttr &AL); + /* TO_UPSTREAM(BoundsSafety) ON */ + QualType + PostProcessBoundsSafetyAllocSizeAttribute(FunctionDecl *EnclosingDecl, + QualType FTy); + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// DeclClonePragmaWeak - clone existing decl (maybe definition), /// \#pragma weak needs a non-definition decl and source may not have one. NamedDecl *DeclClonePragmaWeak(NamedDecl *ND, const IdentifierInfo *II, diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index 093fe0e7c431a..198ec82c2fda1 100644 --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -541,6 +541,11 @@ void clang::FormatASTNodeDiagnosticArgument( } } + /* TO_UPSTREAM(BoundsSafety) ON */ + if (Modifier == "unquoted") + NeedQuotes = false; + /* TO_UPSTREAM(BoundsSafety) OFF */ + if (NeedQuotes) { Output.insert(Output.begin()+OldEnd, '\''); Output.push_back('\''); diff --git a/clang/lib/Sema/BoundsSafetySuggestions.cpp b/clang/lib/Sema/BoundsSafetySuggestions.cpp index 35f33d9efa28e..7e574c2e15f7f 100644 --- a/clang/lib/Sema/BoundsSafetySuggestions.cpp +++ b/clang/lib/Sema/BoundsSafetySuggestions.cpp @@ -490,22 +490,6 @@ bool UnsafeOperationVisitor::FindSingleEntity( // 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())); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index fafde666c52ac..b2c3698b1f08f 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2951,6 +2951,10 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.HLSL().mergeVkConstantIdAttr(D, *CI, CI->getId()); else if (const auto *SA = dyn_cast(Attr)) NewAttr = S.HLSL().mergeShaderAttr(D, *SA, SA->getType()); + /* TO_UPSTREAM(BoundsSafety) ON */ + else if (const auto *ASA = dyn_cast(Attr)) + NewAttr = S.mergeAllocSizeAttr(D, *ASA); + /* TO_UPSTREAM(BoundsSafety) OFF */ else if (isa(Attr)) // Do nothing. Each redeclaration should be suppressed separately. NewAttr = nullptr; @@ -3818,6 +3822,34 @@ static void fixBoundsSafetyFunctionDecl(Sema &S, Sema::SemaDiagnosticBuilder &D, } } +static bool diagnoseConflictingAllocSizeAttribute(FunctionDecl *New, + FunctionDecl *Old, + Sema &Self) { + // System headers that haven't adopted bounds safety are allowed to break + // the rules for compatibility reasons, since errors there are hard to fix. + if (Self.allowBoundsUnsafeAssignment(New->getLocation())) + return true; + + // Different alloc_size attributes will lead to mismatching __sized_by_or_null + // return types. Merging of attributes is done after type checking, but we + // want to emit the root cause of the type error rather than an error for + // mismatcing (implicit) return types. + if (auto OldASA = Old->getAttr()) { + if (auto NewASA = New->getAttr()) { + if (!OldASA->getElemSizeParam().equals(NewASA->getElemSizeParam()) || + !OldASA->getNumElemsParam().equals(NewASA->getNumElemsParam())) { + Self.Diag(NewASA->getLoc(), diag::err_mismatched_alloc_size) + << NewASA->getRange(); + Self.Diag(OldASA->getLoc(), diag::note_conflicting_attribute) + << OldASA->getRange(); + return false; + } + } + } + + return true; +} + /// 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. @@ -3825,7 +3857,8 @@ static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, FunctionDecl *Old, Sema &Self) { std::function checkCompatibleBoundExprs; - checkCompatibleBoundExprs = [&](const Expr *NewExpr, const Expr *OldExpr) -> bool { + checkCompatibleBoundExprs = [&](const Expr *NewExpr, + const Expr *OldExpr) -> bool { llvm::FoldingSetNodeID NewID; llvm::FoldingSetNodeID OldID; if (NewExpr) @@ -3836,7 +3869,28 @@ static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, return true; return false; }; - std::unique_ptr D; + + std::function + newFuncWillInheritOldReturnType = [](FunctionDecl *New, + FunctionDecl *Old) { + QualType NewRetTy = New->getReturnType(); + if (!NewRetTy->isPointerType()) + return false; + if (NewRetTy->isSafePointerType() && + !NewRetTy->isImplicitSafePointerType()) + return false; + QualType OldRetTy = Old->getReturnType(); + if (!OldRetTy->isPointerType()) + return false; + if (!OldRetTy->isImplicitSafePointerType()) + return false; + assert(OldRetTy->isSafePointerType()); + + bool Ret = + !New->hasAttr() && Old->hasAttr(); + return Ret; + }; + std::function checkCompatibleDynamicBoundTypes = @@ -3848,6 +3902,8 @@ static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, const auto *OldDCPTy = OldTy->getAs(); const auto *NewDRPTy = NewTy->getAs(); const auto *OldDRPTy = OldTy->getAs(); + const bool NewIsEndedBy = NewDRPTy && NewDRPTy->getEndPointer(); + const bool OldIsEndedBy = OldDRPTy && OldDRPTy->getEndPointer(); const auto *NewVTTy = NewTy->getAs(); const auto *OldVTTy = OldTy->getAs(); const unsigned CountDiagIndex = 0; @@ -3858,14 +3914,11 @@ static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, 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); - } + Sema::SemaDiagnosticBuilder D( + 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; @@ -3888,12 +3941,41 @@ static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, ReportConflict(Index); }; + auto ReportCountBothConflict = [&](const CountAttributedType *NewDCPTy, + const CountAttributedType *OldDCPTy) { + ReportCountConflict(NewDCPTy); + + Self.Diag(NewLoc, diag::note_bounds_safety_conflicting_count_attribute) + << OldDCPTy->getKind() << OldDCPTy->getCountExpr() + << NewDCPTy->getKind() << NewDCPTy->getCountExpr(); + + if (auto NewASA = New->getAttr()) { + Self.Diag(NewASA->getLoc(), + diag::note_bounds_safety_implicit_size_from_alloc_size_attr) + << !New->hasAttr() << NewASA->getRange(); + } + + if (auto OldASA = Old->getAttr()) { + Self.Diag(OldASA->getLoc(), + diag::note_bounds_safety_implicit_size_from_alloc_size_attr) + << !Old->hasAttr() << OldASA->getRange(); + } + }; + + // Check bounds type present in New but not in Old first, then + // in Old but now in New. That way if a parameter or return type + // is e.g. __counted_by in Old and __null_terminated in New, we + // report an error for mismatch on __null_terminated rather than + // __counted_by, since it's more local to the error being emitted. + if (NewDCPTy && !OldDCPTy) { ReportCountConflict(NewDCPTy); return false; } - if (NewDRPTy && !OldDRPTy) { + // If implicit __started_by is missing, we have already emitted an error for + // the __ended_by mismatch, so don't diagnose that. + if (NewIsEndedBy && !OldIsEndedBy) { ReportConflict(RangeDiagIndex); return false; } @@ -3908,9 +3990,8 @@ static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, 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()) { + + if (!NewIsEndedBy && OldIsEndedBy) { ReportConflict(RangeDiagIndex); return false; } @@ -3922,27 +4003,31 @@ static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, if (NewDCPTy && OldDCPTy) { if (!checkCompatibleBoundExprs(NewDCPTy->getCountExpr(), OldDCPTy->getCountExpr())) { - ReportCountConflict(NewDCPTy); + ReportCountBothConflict(NewDCPTy, OldDCPTy); 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); + ReportCountBothConflict(NewDCPTy, OldDCPTy); return false; } } // '__counted_by_or_null' and '__counted_by' if (NewDCPTy->isOrNull() != OldDCPTy->isOrNull()) { - ReportCountConflict(NewDCPTy); + ReportCountBothConflict(NewDCPTy, OldDCPTy); return false; } } - if (NewDRPTy && OldDRPTy) { + if (NewIsEndedBy && OldIsEndedBy) { // Start pointers are implicit so we don't check it here. if (!checkCompatibleBoundExprs(NewDRPTy->getEndPointer(), OldDRPTy->getEndPointer())) { ReportConflict(RangeDiagIndex); + Self.Diag(NewLoc, + diag::note_bounds_safety_conflicting_pointer_attribute_args) + << /* end */ 0 << OldDRPTy->getEndPointer() + << NewDRPTy->getEndPointer(); return false; } } @@ -3950,6 +4035,9 @@ static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, !llvm::APSInt::isSameValue(NewVTTy->getTerminatorValue(Self.Context), OldVTTy->getTerminatorValue(Self.Context))) { ReportConflict(TermDiagIndex); + Self.Diag(NewLoc, diag::note_bounds_safety_conflicting_pointer_attribute_args) + << /* terminator */ 1 << OldVTTy->getTerminatorExpr() + << NewVTTy->getTerminatorExpr(); return false; } TypeLoc NewSubTL, OldSubTL; @@ -3973,9 +4061,10 @@ static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, OldReturnTL = OldTL.getReturnLoc(); if (auto NewTL = New->getFunctionTypeLoc()) NewReturnTL = NewTL.getReturnLoc(); - bool Compatible = checkCompatibleDynamicBoundTypes( - New->getReturnType(), Old->getReturnType(), NewReturnTL, OldReturnTL, - New->getBeginLoc(), Old->getBeginLoc()); + bool Compatible = newFuncWillInheritOldReturnType(New, Old) || + 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) { @@ -3988,8 +4077,7 @@ static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, NewParam->getBeginLoc(), OldParam->getBeginLoc()); } - if (D) { - D.reset(); + if (!Compatible) { Self.Diag(Old->getBeginLoc(), diag::note_previous_declaration); } @@ -4565,7 +4653,11 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, return true; } - /*TO_UPSTREAM(BoundsSafety) ON */ + /* TO_UPSTREAM(BoundsSafety) ON */ + if (getLangOpts().BoundsSafety && + !diagnoseConflictingAllocSizeAttribute(New, Old, *this)) + return true; + 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 @@ -4576,7 +4668,9 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, if (!diagnoseFunctionConflictWithDynamicBoundTypes(New, Old, *this)) return true; } else if (mergeFunctionDeclBoundsAttributes(New, Old, *this)) { - NewQType = New->getType(); + // Stripping sugar is required to inherit alloc_size attribute. + // Bounds attributes are not stripped from the function decl. + NewQType = Context.getCanonicalType(New->getType()); } } @@ -4584,7 +4678,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, mergeFunctionDeclTerminatedByAttribute(New, Old, *this)) { // TODO: Merge __terminated_by() attributes in attribute-only mode. // rdar://137984921 - NewQType = New->getType(); + NewQType = Context.getCanonicalType(New->getType()); } /*TO_UPSTREAM(BoundsSafety) OFF */ @@ -5001,8 +5095,19 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, = New->getType()->getAs(); // Determine whether this is the GNU C extension. - QualType MergedReturn = Context.mergeTypes(OldProto->getReturnType(), - NewProto->getReturnType()); + /* TO_UPSTREAM(BoundsSafety) ON + * Diff w/ upstream: reversed parameter order. This aligns better with other + * calls to mergeTypes, and makes sure that the new type is used if they are + * canonically the same. This is relevant for bounds safety when overriding an + * earlier declaration with different bounds. */ + QualType MergedReturn; + if (getLangOpts().BoundsSafety) + MergedReturn = Context.mergeTypes(NewProto->getReturnType(), + OldProto->getReturnType()); + else + /* TO_UPSTREAM(BoundsSafety) OFF */ + MergedReturn = Context.mergeTypes(OldProto->getReturnType(), + NewProto->getReturnType()); bool LooseCompatible = !MergedReturn.isNull(); for (unsigned Idx = 0, End = Old->getNumParams(); LooseCompatible && Idx != End; ++Idx) { @@ -5121,7 +5226,17 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old, // Merge the function types so the we get the composite types for the return // and argument types. Per C11 6.2.7/4, only update the type if the old decl // was visible. - QualType Merged = Context.mergeTypes(Old->getType(), New->getType()); + /* TO_UPSTREAM(BoundsSafety) ON + * Diff w/ upstream: reversed parameter order. This aligns better with other + * calls to mergeTypes, and makes sure that the new type is used if they are + * canonically the same. This is relevant for bounds safety when overriding an + * earlier declaration with different bounds. */ + QualType Merged; + if (getLangOpts().BoundsSafety) + Merged = Context.mergeTypes(New->getType(), Old->getType()); + else + Merged = Context.mergeTypes(Old->getType(), New->getType()); + /* TO_UPSTREAM(BoundsSafety) OFF */ if (!Merged.isNull() && MergeTypeWithOld) New->setType(Merged); @@ -7718,7 +7833,7 @@ void Sema::deduceBoundsSafetyPointerTypes(ValueDecl *Decl) { Decl->getType(), CurPointerAbi, ShouldAutoBound)); } -static QualType deduceBoundsSafetyFuncType(Sema &S, const FunctionDecl *Decl, +static QualType deduceBoundsSafetyFuncType(Sema &S, FunctionDecl *Decl, QualType Ty) { if (const auto *AttrTy = Ty->getAs()) { QualType ModTy = @@ -7731,6 +7846,7 @@ static QualType deduceBoundsSafetyFuncType(Sema &S, const FunctionDecl *Decl, QualType NewRetTy = S.Context.getBoundsSafetyAutoPointerType( Decl->getReturnType(), S.CurPointerAbi, /*ShouldAutoBound=*/false); + NewRetTy = S.PostProcessBoundsSafetyAllocSizeAttribute(Decl, NewRetTy); if (NewRetTy == Decl->getReturnType()) return Ty; @@ -8049,8 +8165,9 @@ static void checkExternalBoundsRedeclaration(Sema &S, Decl *D) { if (!NewVD || NewVD->isFirstDecl()) return; - if (NewVD->isFirstDecl()) - return; + // Parameters are handled in diagnoseFunctionConflictWithDynamicBoundTypes(). + assert(!isa(NewVD)); + VarDecl *PrevVD = NewVD->getFirstDecl(); const auto *PrevTy = PrevVD->getType()->getAs(); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 71f27992769d0..ae774747e4aeb 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -568,6 +568,121 @@ static bool checkParamIsIntegerType(Sema &S, const Decl *D, const AttrInfo &AI, return true; } +/* TO_UPSTREAM(BoundsSafety) ON */ +static QualType CreateImplicitCountAttributedType(Sema &S, unsigned Level, + const StringRef DiagName, Expr *ArgExpr, + SourceLocation Loc, + bool CountInBytes, + bool OrNull, QualType T, + bool ScopeCheck = false); + +static ExprResult getAllocSizeExpr(Sema &S, Decl *D, ParamIdx SizeIdx, + ParamIdx NumberIdx) { + auto createParamDeclRef = [&S](Decl *FuncD, ParamIdx Idx) { + ParmVarDecl *Parm = getFunctionOrMethodParam(FuncD, Idx.getASTIndex()); + + return DeclRefExpr::Create( + S.Context, NestedNameSpecifierLoc{}, SourceLocation{}, Parm, false, + DeclarationNameInfo{Parm->getDeclName(), Parm->getLocation()}, + Parm->getType(), VK_LValue); + }; + + Expr *SizeExpr = createParamDeclRef(D, SizeIdx); + if (NumberIdx.isValid()) { + Expr *NumExpr = createParamDeclRef(D, NumberIdx); + return S.CreateBuiltinBinOp(SourceLocation(), BO_Mul, SizeExpr, NumExpr); + } + + return SizeExpr; +} + +static QualType PostProcessBoundsSafetyAllocSizeAttributeImpl( + Sema &S, NamedDecl *D, const AllocSizeAttr &ASA, QualType FT) { + ExprResult SizeExpr = + getAllocSizeExpr(S, D, ASA.getElemSizeParam(), ASA.getNumElemsParam()); + if (SizeExpr.isInvalid()) + return FT; + + unsigned Level = 0; + bool CountInBytes = true; + // returns_nonnull is allowed to change program semantics to assume + // it cannot return null. _Nonnull does not affect program semantics, + // only analysis/diagnostics, so ignore that here. + bool OrNull = !D->hasAttr(); + + const IdentifierInfo *AttrName = ASA.getAttrName(); + return CreateImplicitCountAttributedType(S, Level, AttrName->getName(), SizeExpr.get(), + ASA.getLoc(), CountInBytes, OrNull, FT); +} + +void diagnoseInvalidReturnTypeForAllocSize(Sema &S, FunctionDecl *D, + const AllocSizeAttr &ASA) { + ExprResult SizeExpr = + getAllocSizeExpr(S, D, ASA.getElemSizeParam(), ASA.getNumElemsParam()); + if (SizeExpr.isInvalid()) + return; + Expr *Size = SizeExpr.get(); + + QualType RetTy = D->getReturnType(); + auto CATy = RetTy->getAs(); + if (!CATy || !CATy->isCountInBytes()) { + S.Diag(D->getBeginLoc(), diag::err_invalid_return_type_for_alloc_size) + << RetTy << Size << D->getReturnTypeSourceRange(); + S.Diag(ASA.getLoc(), diag::note_attribute_inherited) << ASA.getRange(); + return; + } + + llvm::FoldingSetNodeID NewID; + llvm::FoldingSetNodeID OldID; + CATy->getCountExpr()->IgnoreParenImpCasts()->Profile(NewID, S.Context, + /*Canonical*/ true); + Size->Profile(OldID, S.Context, /*Canonical*/ true); + if (NewID != OldID) { + S.Diag(D->getBeginLoc(), diag::err_invalid_return_type_for_alloc_size) + << RetTy << Size << D->getReturnTypeSourceRange(); + S.Diag(ASA.getLoc(), diag::note_attribute_inherited) << ASA.getRange(); + } +} + +/// Note: this only checks alloc_size attributes on the same declaration. +/// Because type merging is done before attribute merging, alloc_size +/// attributes between redeclarations are checked in +/// diagnoseFunctionConflictWithDynamicBoundTypes. +static AllocSizeAttr *mergeAllocSizeAttrImpl(Sema &S, Decl *D, + const AttributeCommonInfo &CI, + ParamIdx ElemSizeParam, + ParamIdx NumElemsParam) { + if (S.getLangOpts().BoundsSafety) { + if (auto OldASA = D->getAttr()) { + if (!OldASA->getElemSizeParam().equals(ElemSizeParam) || + !OldASA->getNumElemsParam().equals(NumElemsParam)) { + S.Diag(CI.getLoc(), diag::err_mismatched_alloc_size) << CI.getRange(); + S.Diag(OldASA->getLoc(), diag::note_conflicting_attribute) + << OldASA->getRange(); + } + // Don't add a second attribute regardless, it is just redundant + return nullptr; + } + } + + return ::new (S.Context) + AllocSizeAttr(S.Context, CI, ElemSizeParam, NumElemsParam); +} + +AllocSizeAttr *Sema::mergeAllocSizeAttr(NamedDecl *D, + const AllocSizeAttr &ASA) { + assert(ASA.getElemSizeParam().isValid()); + AllocSizeAttr *NewASA = mergeAllocSizeAttrImpl( + *this, D, ASA, ASA.getElemSizeParam(), ASA.getNumElemsParam()); + if (getLangOpts().BoundsSafety) + // If we've reached the merging stage, + // PostProcessBoundsSafetyAllocSizeAttribute has already run. Here we check + // that the return type has bounds that match the semantics of alloc_size + diagnoseInvalidReturnTypeForAllocSize(*this, cast(D), ASA); + + return NewASA; +} + static void handleAllocSizeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!AL.checkAtLeastNumArgs(S, 1) || !AL.checkAtMostNumArgs(S, 2)) return; @@ -601,8 +716,16 @@ static void handleAllocSizeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { NumberArgNo = ParamIdx(Val, D); } - D->addAttr(::new (S.Context) - AllocSizeAttr(S.Context, AL, SizeArgNo, NumberArgNo)); + /* TO_UPSTREAM(BoundsSafety) ON */ + AllocSizeAttr *A = mergeAllocSizeAttrImpl(S, D, AL, SizeArgNo, NumberArgNo); + if (!A) + return; + // Implicit __sized_by_or_null return type will be inferred later in + // PostProcessBoundsSafetyAllocSizeAttribute. This guarantees that all + // explicit bounds attributes are applied first. + /* TO_UPSTREAM(BoundsSafety) OFF */ + + D->addAttr(A); } static bool checkTryLockFunAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL, @@ -6069,6 +6192,7 @@ class ConstructDynamicBoundType bool ScopeCheck; bool AllowRedecl; bool AutoPtrAttributed = false; + bool HasNonnullAttr = false; bool AtomicErrorEmitted = false; public: @@ -6208,6 +6332,9 @@ class ConstructDynamicBoundType llvm::SaveAndRestore AutoPtrAttributedLocal(AutoPtrAttributed); if (T->getAttrKind() == attr::PtrAutoAttr) AutoPtrAttributed = true; + SaveAndRestore HasNonnullAttrLocal(HasNonnullAttr); + if (T->getAttrKind() == attr::TypeNonNull) + HasNonnullAttr = true; QualType NewModTy = Visit(T->getModifiedType()); if (NewModTy.isNull()) @@ -6242,16 +6369,21 @@ class ConstructCountAttributedType : public ConstructDynamicBoundType { bool CountInBytes; bool OrNull; + // Apply PtrAutoAttr on top of the CountAttributedType if it's implicit. + // So far only alloc_size counts as implicit, while arrays with explicit sizes + // being converted to __counted_by pointers still count as explicit. + bool IsImplicit; public: explicit ConstructCountAttributedType(Sema &S, unsigned Level, const StringRef DiagName, Expr *ArgE, SourceLocation Loc, bool CountInBytes, bool OrNull, bool AllowRedecl, - bool ScopeCheck = false) + bool ScopeCheck = false, + bool IsImplicit = false) : ConstructDynamicBoundType(S, Level, DiagName, ArgE, Loc, ScopeCheck, AllowRedecl), - CountInBytes(CountInBytes), OrNull(OrNull) { + CountInBytes(CountInBytes), OrNull(OrNull), IsImplicit(IsImplicit) { if (!ArgExpr->getType()->isIntegralOrEnumerationType()) { S.Diag(Loc, diag::err_attribute_argument_type_for_bounds_safety_count) << DiagName; @@ -6314,11 +6446,13 @@ class ConstructCountAttributedType : OrNull, ScopeCheck); assert(ConstructedType == nullptr); ConstructedType = Ty->getAs(); + if (IsImplicit) + Ty = S.Context.getAttributedType(attr::PtrAutoAttr, Ty, Ty); return Ty; } QualType DiagnoseConflictingType(const CountAttributedType *T) { - if (AllowRedecl) { + if (AllowRedecl || IsImplicit || AutoPtrAttributed) { 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 @@ -6332,12 +6466,60 @@ class ConstructCountAttributedType : if (const auto *OldCnt = T->getCountExpr()) OldCnt->Profile(OldID, S.Context, /*Canonical*/ true); - if (NewID == OldID) + if (NewID == OldID && NewDCPTy->getKind() == T->getKind()) return NewTy; + + if (IsImplicit && NewID == OldID && + NewDCPTy->isCountInBytes() == T->isCountInBytes()) { + assert(NewDCPTy->isCountInBytes() && + "unexpected implicit __counted_by_or_null"); + + if (!NewDCPTy->isOrNull()) + // Error already emitted for combining returns_nonnull with + // __sized_by_or_null. No need to clutter with this warning. + return QualType(); + + // Ignore implicit __sized_by_or_null when explicit __sized_by exists. + assert(!T->isOrNull()); + + if (HasNonnullAttr) + return QualType(); // warning silenced by combining __sized_by and + // _Nonnull + + S.Diag(Loc, + diag::warn_bounds_safety_ignored_implicit_sized_by_or_null); + S.Diag(Loc, + diag::note_bounds_safety_implicit_size_from_alloc_size_attr) + << OrNull; + S.Diag(T->getCountExpr()->getExprLoc(), diag::note_previous_attribute); + S.Diag( + T->getCountExpr()->getExprLoc(), + diag::note_bounds_safety_overriding_implicit_sized_by_or_null_silence); + return QualType(); + } } S.Diag(Loc, diag::err_bounds_safety_conflicting_pointer_attributes) << /* pointer */ T->isPointerType() << /* count */ 2; + + unsigned DiagIdx = CountInBytes; + if (OrNull) + DiagIdx += 2; + + S.Diag(Loc, diag::note_bounds_safety_conflicting_count_attribute) + << T->getKind() << T->getCountExpr() << DiagIdx << ArgExpr; + + if (IsImplicit) { + S.Diag(Loc, + diag::note_bounds_safety_implicit_size_from_alloc_size_attr) + << OrNull; + } + + S.Diag(T->getCountExpr()->getBeginLoc(), // FIXME: would like location and + // range of attribute instead of + // just count arg rdar://127087868 + diag::note_previous_attribute); + return QualType(); } @@ -6530,7 +6712,6 @@ class MakeStartedByPointerType class ConstructDynamicRangePointerType : public ConstructDynamicBoundType { - using BaseClass = ConstructDynamicBoundType; std::optional StartPtrInfo; public: @@ -6619,7 +6800,7 @@ class ConstructDynamicRangePointerType : ConstructedType = RetTy->getAs(); return RetTy; } - return BaseClass::VisitDynamicRangePointerType(T); + return VisitDynamicRangePointerType(T); } QualType DiagnoseConflictingType(const CountAttributedType *T) { @@ -6650,6 +6831,18 @@ class ConstructDynamicRangePointerType : }; } // namespace +static QualType CreateImplicitCountAttributedType(Sema &S, unsigned Level, + const StringRef DiagName, Expr *ArgExpr, + SourceLocation Loc, + bool CountInBytes, + bool OrNull, QualType T, + bool ScopeCheck) { + return ConstructCountAttributedType(S, Level, DiagName, ArgExpr, Loc, CountInBytes, + OrNull, /* AllowRedecl */ false, ScopeCheck, + /* IsImplicit */ true) + .Visit(T); +} + Sema::LifetimeCheckKind Sema::getLifetimeCheckKind(const VarDecl *Var) { if (!Var) return Sema::LifetimeCheckKind::None; @@ -9574,6 +9767,23 @@ bool Sema::ProcessAccessDeclAttributeList( return false; } +/* TO_UPSTREAM(BoundsSafety) ON */ +QualType Sema::PostProcessBoundsSafetyAllocSizeAttribute(FunctionDecl *D, + QualType FT) { + if (getLangOpts().BoundsSafety) { + auto ASA = D->getAttr(); + if (!ASA) + return FT; + QualType NewDeclTy = + PostProcessBoundsSafetyAllocSizeAttributeImpl(*this, D, *ASA, FT); + if (NewDeclTy.isNull()) + return FT; + return NewDeclTy; + } + return FT; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// checkUnusedDeclAttributes - Check a list of attributes to see if it /// contains any decl attributes that we should warn about. static void checkUnusedDeclAttributes(Sema &S, const ParsedAttributesView &A) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 23c46a7da9c67..86fcc541ac87b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -25612,17 +25612,12 @@ ExprResult Sema::ActOnBoundsSafetyCall(ExprResult Call) { return Call; // 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.) + // there's a dynamic bound pointer type in this function prototype.) 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; - } + if (!ContainsBoundsAttributedType::check(FT)) + return CallE; Expr *ResultExpr = CallE; SmallVector OVEs; @@ -25755,32 +25750,6 @@ ExprResult Sema::ActOnBoundsSafetyCall(ExprResult Call) { *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(); - } - - ExprResult SizeRValue = DefaultLvalueConversion(SizeExpr); - if (!SizeRValue.get()) - return ExprError(); - - 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(); - } - } } if (!OVEs.empty()) { diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 7a8e134d61de5..1c9d41a6c4229 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -9410,7 +9410,7 @@ static bool HandlePtrTerminatedByTypeAttr(TypeProcessingState &state, << T->isPointerType() << /* terminator */ 4; S.Diag(PAttr.getLoc(), diag::note_bounds_safety_conflicting_pointer_attribute_args) - << /* terminator */ 2 << VT->getTerminatorExpr() << TerminatorExpr; + << /* terminator */ 1 << VT->getTerminatorExpr() << TerminatorExpr; PAttr.setInvalid(); return false; } diff --git a/clang/test/BoundsSafety-legacy-checks/AST/count-attrs.c b/clang/test/BoundsSafety-legacy-checks/AST/count-attrs.c index fa91c43e8af00..82cf19b6f7ab0 100644 --- a/clang/test/BoundsSafety-legacy-checks/AST/count-attrs.c +++ b/clang/test/BoundsSafety-legacy-checks/AST/count-attrs.c @@ -40,7 +40,7 @@ int *__sized_by(len) byte_frob_body(int len) { return 0; } // 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:|-FunctionDecl {{.*}} alloc_bytes 'void *__single __sized_by_or_null(byte_count)(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]' 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 index 83450c6d4f38c..c25aab073f395 100644 --- 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 @@ -1,2 +1,4 @@ -void *myalloc(unsigned); -void *memcpy(void *, void *, unsigned long long); \ No newline at end of file +#include +void *myalloc(unsigned size2); +void * __single myalloc2(unsigned size2); +void *memcpy(void *, void *, unsigned long long); 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 index 00f4859f1d735..1aaed73ab72a1 100644 --- 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 @@ -1 +1,5 @@ -void *myalloc(unsigned) __attribute__((alloc_size(1))); \ No newline at end of file +#include +void *myalloc(unsigned) __attribute__((alloc_size(1))); +void * __sized_by_or_null(size1) myalloc2(unsigned size1); +void *myalloc3(unsigned size1) __attribute__((alloc_size(1))); +void * __sized_by_or_null(size1) myalloc4(unsigned size1); diff --git a/clang/test/BoundsSafety/AST/alloc-size-attr-dup.c b/clang/test/BoundsSafety/AST/alloc-size-attr-dup.c new file mode 100644 index 0000000000000..855066166df49 --- /dev/null +++ b/clang/test/BoundsSafety/AST/alloc-size-attr-dup.c @@ -0,0 +1,24 @@ +// RUN: not %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -x objective-c -fexperimental-bounds-safety-objc -ast-dump %s 2>&1 | FileCheck %s + +__attribute__((alloc_size(1))) __attribute__((alloc_size(1))) void * dup_attr(unsigned); + +// CHECK: |-FunctionDecl {{.*}} dup_attr 'void *__single __sized_by_or_null(function-parameter-0-0)(unsigned int)' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} 'unsigned int' +// CHECK-NEXT: | `-AllocSizeAttr {{.*}} 1 +// +// CHECK-NOT: AllocSizeAttr + +__attribute__((alloc_size(1, 2))) __attribute__((alloc_size(2, 1))) void * mismatch_attr(unsigned, unsigned); +void * mismatch_attr(unsigned, unsigned); + +// CHECK-NEXT: |-FunctionDecl{{.*}} mismatch_attr 'void *__single __sized_by_or_null(function-parameter-0-0 * function-parameter-0-1)(unsigned int, unsigned int)' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} 'unsigned int' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} 'unsigned int' +// CHECK-NEXT: | `-AllocSizeAttr {{.*}} 1 2 +// CHECK-NEXT: `-FunctionDecl {{.*}} mismatch_attr 'void *__single(unsigned int, unsigned int)' +// CHECK-NEXT: |-ParmVarDecl {{.*}} 'unsigned int' +// CHECK-NEXT: |-ParmVarDecl {{.*}} 'unsigned int' +// CHECK-NEXT: `-AllocSizeAttr {{.*}} Inherited 1 2 +// +// CHECK-NOT: AllocSizeAttr diff --git a/clang/test/BoundsSafety/AST/alloc-size-attr-sized-by-or-null-sys-header-override.c b/clang/test/BoundsSafety/AST/alloc-size-attr-sized-by-or-null-sys-header-override.c new file mode 100644 index 0000000000000..27e07f799336d --- /dev/null +++ b/clang/test/BoundsSafety/AST/alloc-size-attr-sized-by-or-null-sys-header-override.c @@ -0,0 +1,152 @@ +// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -ast-dump %s 2>&1 | FileCheck %S/alloc-size-attr-sized-by-or-null-sys-header.h +// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -ast-dump %s 2>&1 | FileCheck --check-prefix CHECK-HEADER %s +// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s + +// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -x objective-c -fexperimental-bounds-safety-objc -ast-dump %s 2>&1 | FileCheck %S/alloc-size-attr-sized-by-or-null-sys-header.h +// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -x objective-c -fexperimental-bounds-safety-objc -ast-dump %s 2>&1 | FileCheck --check-prefix CHECK-HEADER %s +// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -x objective-c -fexperimental-bounds-safety-objc -ast-dump %s 2>&1 | FileCheck %s + +#include +#include "alloc-size-attr-sized-by-or-null-sys-header.h" + +// This makes sure that checks with this prefix don't capture anything from the header +// CHECK-LABEL: NON_HEADER +void NON_HEADER(); + +__attribute__((alloc_size(1))) void * unnamed_param(unsigned size) { + return (void*)0; +} +// CHECK-HEADER: |-FunctionDecl [[func_unnamed_param:0x[^ ]+]] {{.+}} unnamed_param 'void *__single __sized_by_or_null(function-parameter-0-0)(unsigned int)' +// CHECK-HEADER: | |-ParmVarDecl +// CHECK-HEADER: | `-AllocSizeAttr + +// CHECK-LABEL: unnamed_param +// CHECK-SAME: 'void *__single __sized_by_or_null(size)(unsigned int)' +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size_9:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-CompoundStmt +// CHECK-NEXT: {{^}}| | `-ReturnStmt +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by_or_null(size)':'void *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_size_9]] +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_3]] {{.*}} 'void *' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned int' +// CHECK: {{^}}| `-AllocSizeAttr + +void * __sized_by_or_null(size) inherit_attr(unsigned size) { + return (void*)0; +} + +// CHECK-HEADER: |-FunctionDecl [[func_inherit_attr:0x[^ ]+]] {{.+}} inherit_attr 'void *__single __sized_by_or_null(size)(unsigned int)' +// CHECK-HEADER: | |-ParmVarDecl [[var_size_8:0x[^ ]+]] +// CHECK-HEADER: | `-AllocSizeAttr + +// CHECK-LABEL: inherit_attr +// CHECK-SAME: 'void *__single __sized_by_or_null(size)(unsigned int)' +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size_10:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-CompoundStmt +// CHECK-NEXT: {{^}}| | `-ReturnStmt +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by_or_null(size)':'void *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}}| | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_size_10]] +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_5]] {{.*}} 'void *' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_6]] {{.*}} 'unsigned int' +// CHECK: {{^}}| `-AllocSizeAttr + +void * unnamed_param_inherit_attr(unsigned size) { + return (void*)0; +} + +// CHECK-HEADER: |-FunctionDecl [[func_unnamed_param_inherit_attr:0x[^ ]+]] {{.+}} unnamed_param_inherit_attr 'void *__single __sized_by_or_null(function-parameter-0-0)(unsigned int)' +// CHECK-HEADER: | |-ParmVarDecl +// CHECK-HEADER: | `-AllocSizeAttr + +// rdar://131622509 This type is being overridden by the system header +// CHECK-LABEL: unnamed_param_inherit_attr +// CHECK-SAME: 'void *__single __sized_by_or_null(size)(unsigned int)' +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size_11:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-CompoundStmt +// CHECK-NEXT: {{^}}| | `-ReturnStmt +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by_or_null(size)':'void *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_7]] +// CHECK-NEXT: {{^}}| | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_size_11]] +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_7]] {{.*}} 'void *' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_8]] {{.*}} 'unsigned int' +// CHECK: {{^}}| `-AllocSizeAttr + +void * override_nullability(unsigned, unsigned) __attribute__((returns_nonnull)) __attribute__((alloc_size(1, 2))); + +// CHECK-HEADER: |-FunctionDecl [[func_override_nullability:0x[^ ]+]] {{.+}} override_nullability 'void *__single __sized_by_or_null(function-parameter-0-0 * function-parameter-0-1)(unsigned int, unsigned int)' +// CHECK-HEADER: | |-ParmVarDecl +// CHECK-HEADER: | |-ParmVarDecl +// CHECK-HEADER: | `-AllocSizeAttr + +// CHECK-LABEL: override_nullability +// CHECK-SAME: 'void *__single __sized_by(function-parameter-0-0 * function-parameter-0-1)(unsigned int, unsigned int)' +// CHECK-NEXT: {{^}}| |-ParmVarDecl +// CHECK-NEXT: {{^}}| |-ParmVarDecl +// CHECK-NEXT: {{^}}| |-ReturnsNonNullAttr +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +void * override_nullability2(unsigned, unsigned) __attribute__((alloc_size(1, 2))); + +// CHECK-HEADER: |-FunctionDecl [[func_override_nullability2:0x[^ ]+]] {{.+}} override_nullability2 'void *__single __sized_by(function-parameter-0-0 * function-parameter-0-1)(unsigned int, unsigned int)' +// CHECK-HEADER: | |-ParmVarDecl +// CHECK-HEADER: | |-ParmVarDecl +// CHECK-HEADER: | |-ReturnsNonNullAttr +// CHECK-HEADER: | `-AllocSizeAttr + +// CHECK-LABEL: override_nullability2 +// CHECK-SAME: 'void *__single __sized_by_or_null(function-parameter-0-0 * function-parameter-0-1)(unsigned int, unsigned int)' +// CHECK-NEXT: {{^}}| |-ParmVarDecl +// CHECK-NEXT: {{^}}| |-ParmVarDecl +// CHECK-NEXT: {{^}}| |-ReturnsNonNullAttr +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +__attribute__((alloc_size(1))) void * unnamed_param_non_sys(unsigned); +__attribute__((alloc_size(1))) void * unnamed_param_non_sys(unsigned size) { + return (void*)0; +} + +// CHECK: |-FunctionDecl [[func_unnamed_param_non_sys:0x[^ ]+]] {{.+}} unnamed_param_non_sys 'void *__single __sized_by_or_null(function-parameter-0-0)(unsigned int)' +// CHECK-NEXT: {{^}}| |-ParmVarDecl +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +// CHECK: `-FunctionDecl [[func_unnamed_param_non_sys_1:0x[^ ]+]] {{.+}} unnamed_param_non_sys 'void *__single __sized_by_or_null(size)(unsigned int)' +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_size_12:0x[^ ]+]] +// CHECK-NEXT: {{^}} |-CompoundStmt +// CHECK-NEXT: {{^}} | `-ReturnStmt +// CHECK-NEXT: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by_or_null(size)':'void *__single' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_9]] +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_size_12]] +// CHECK-NEXT: {{^}} | |-OpaqueValueExpr [[ove_9]] {{.*}} 'void *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_10]] {{.*}} 'unsigned int' +// CHECK: {{^}} `-AllocSizeAttr + diff --git a/clang/test/BoundsSafety/AST/alloc-size-attr-sized-by-or-null-sys-header.h b/clang/test/BoundsSafety/AST/alloc-size-attr-sized-by-or-null-sys-header.h new file mode 100644 index 0000000000000..31d80c57199f1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/alloc-size-attr-sized-by-or-null-sys-header.h @@ -0,0 +1,111 @@ +#pragma clang system_header + +void * __sized_by_or_null(size) explicitly_sized(int size) __attribute__((alloc_size(1))); + +// CHECK: {{^}}|-FunctionDecl [[func_explicitly_sized:0x[^ ]+]] {{.+}} explicitly_sized +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +void * __sized_by(size) explicitly_sized_nonnull(int size) __attribute__((alloc_size(1))); + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_explicitly_sized_nonnull:0x[^ ]+]] {{.+}} explicitly_sized_nonnull +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +void * _Nonnull implicitly_sized_nonnull_non_semantic(int size) __attribute__((alloc_size(1))); + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_implicitly_sized_nonnull_non_semantic:0x[^ ]+]] {{.+}} implicitly_sized_nonnull_non_semantic +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +void * implicitly_sized_nonnull_semantic(int size) __attribute__((returns_nonnull)) __attribute__((alloc_size(1))); +// +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_implicitly_sized_nonnull_semantic:0x[^ ]+]] {{.+}} implicitly_sized_nonnull_semantic +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-ReturnsNonNullAttr +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +void * implicitly_sized_with_count(int size, int count) __attribute__((alloc_size(1, 2))); + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_implicitly_sized_with_count:0x[^ ]+]] {{.+}} implicitly_sized_with_count +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +void * __sized_by(size * count) explicitly_sized_nonnull_with_count(int size, int count) __attribute__((alloc_size(1, 2))); + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_explicitly_sized_nonnull_with_count:0x[^ ]+]] {{.+}} explicitly_sized_nonnull_with_count +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +void * _Nonnull implicitly_sized_nonnull_non_semantic_with_count(int size, int count) __attribute__((alloc_size(1, 2))); + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_implicitly_sized_nonnull_non_semantic_with_count:0x[^ ]+]] {{.+}} implicitly_sized_nonnull_non_semantic_with_count +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +void * implicitly_sized_nonnull_semantic_with_count(int size, int count) __attribute__((returns_nonnull)) __attribute__((alloc_size(1, 2))); + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_implicitly_sized_nonnull_semantic_with_count:0x[^ ]+]] {{.+}} implicitly_sized_nonnull_semantic_with_count +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-ReturnsNonNullAttr +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +void * unnamed_param_with_count(unsigned, unsigned) __attribute__((alloc_size(1, 2))); + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_unnamed_param_with_count:0x[^ ]+]] {{.+}} unnamed_param_with_count +// CHECK-NEXT: {{^}}| |-ParmVarDecl +// CHECK-NEXT: {{^}}| |-ParmVarDecl +// CHECK-NEXT: {{^}}| `-AllocSizeAttr + +__attribute__((alloc_size(1, 2))) void * unnamed_param_with_count(unsigned, unsigned) { + return (void*)0; +} + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_unnamed_param_with_count_1:0x[^ ]+]] {{.+}} unnamed_param_with_count +// CHECK-NEXT: {{^}}| |-ParmVarDecl +// CHECK-NEXT: {{^}}| |-ParmVarDecl +// CHECK-NEXT: {{^}}| |-CompoundStmt +// CHECK-NEXT: {{^}}| | `-ReturnStmt +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by_or_null(function-parameter-0-0 * function-parameter-0-1)':'void *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_2]] {{.*}} 'unsigned int' +// CHECK: {{^}}| `-AllocSizeAttr + +/* --- checked in C file --- */ + +void * unnamed_param(unsigned) __attribute__((alloc_size(1))); + +void * inherit_attr(unsigned size) __attribute__((alloc_size(1))); + +void * unnamed_param_inherit_attr(unsigned) __attribute__((alloc_size(1))); + +void * unnamed_param_with_count_flipped(unsigned, unsigned) + __attribute__((alloc_size(1,2))); + +void * unnamed_param_with_count_mismatching_attrs_after_param_list(unsigned, unsigned) + __attribute__((alloc_size(1,2))); + +void * override_nullability(unsigned, unsigned) + __attribute__((alloc_size(1, 2))); + +void * override_nullability2(unsigned, unsigned) + __attribute__((returns_nonnull)) + __attribute__((alloc_size(1, 2))); + 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 index 67410f2d7ce3f..76a0a013c29ca 100644 --- a/clang/test/BoundsSafety/AST/alloc-sized-calloc/alloc-sized-calloc.c +++ b/clang/test/BoundsSafety/AST/alloc-sized-calloc/alloc-sized-calloc.c @@ -35,13 +35,13 @@ int foo() { // CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' -// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__single __sized_by_or_null(count * size)':'void *__single' // 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]] {{.*}} 'void *__single __sized_by_or_null(count * size)':'void *__single' // CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' // CHECK: {{^}} | | |-OpaqueValueExpr [[ove_1]] // CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' @@ -51,7 +51,7 @@ int foo() { // CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_siz]] // CHECK: {{^}} | | |-OpaqueValueExpr [[ove]] // CHECK: {{^}} | | | `-CallExpr -// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(int, int)' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by_or_null(count * size)(*__single)(int, int)' // CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_my_calloc]] // CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' // CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' @@ -61,7 +61,7 @@ int foo() { // CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' // CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' // CHECK: {{^}} | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' -// CHECK: {{^}} | |-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by_or_null(count * size)':'void *__single' // CHECK: {{^}} | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' // CHECK: {{^}} |-DeclStmt // CHECK: {{^}} | `-VarDecl [[var_ptr2:0x[^ ]+]] @@ -71,13 +71,13 @@ int foo() { // CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' -// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *__single __sized_by_or_null(count * size)':'void *__single' // 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_4]] {{.*}} 'void *__single __sized_by_or_null(count * size)':'void *__single' // CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' // CHECK: {{^}} | | |-OpaqueValueExpr [[ove_5]] // CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' @@ -87,7 +87,7 @@ int foo() { // CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_siz]] // CHECK: {{^}} | | |-OpaqueValueExpr [[ove_4]] // CHECK: {{^}} | | | `-CallExpr -// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(int, int)' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by_or_null(count * size)(*__single)(int, int)' // CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_my_calloc]] // CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' // CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' @@ -97,7 +97,7 @@ int foo() { // CHECK: {{^}} | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' // CHECK: {{^}} | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' // CHECK: {{^}} | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' -// CHECK: {{^}} | |-OpaqueValueExpr [[ove_4]] {{.*}} 'void *' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by_or_null(count * size)':'void *__single' // CHECK: {{^}} | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' // CHECK: {{^}} `-ReturnStmt // CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int' diff --git a/clang/test/BoundsSafety/AST/system-count-return.c b/clang/test/BoundsSafety/AST/system-count-return.c index 8bfda4e706fb8..bcf713aaf5c47 100644 --- a/clang/test/BoundsSafety/AST/system-count-return.c +++ b/clang/test/BoundsSafety/AST/system-count-return.c @@ -43,11 +43,11 @@ int Test() { // CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' -// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' // CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' // CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' // CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' -// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' // CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' // CHECK: {{^}} | | |-OpaqueValueExpr [[ove_3]] // CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' @@ -57,11 +57,11 @@ int Test() { // CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 10 // CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] // CHECK: {{^}} | | `-CallExpr -// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *(*__single)(int)' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)(*__single)(int)' // CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[func_alloc_attributed]] // CHECK: {{^}} | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' // CHECK: {{^}} | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' -// CHECK: {{^}} | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' return bufBound[10]; } 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 index 242c40692fe3e..246978e7f6407 100644 --- a/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr-2.c +++ b/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr-2.c @@ -22,19 +22,19 @@ void Test(unsigned siz) { // CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' -// CHECK: {{^}} | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__single __sized_by_or_null(function-parameter-0-0)':'void *__single' // CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *' // CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'char *' '+' // CHECK: {{^}} | | | |-CStyleCastExpr {{.+}} 'char *' -// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by_or_null(function-parameter-0-0)':'void *__single' // 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: {{^}} | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by_or_null(function-parameter-0-0)(*__single)(unsigned int)' // CHECK: {{^}} | | `-DeclRefExpr {{.+}} 'myalloc' // CHECK: {{^}} | `-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' // CHECK: {{^}} |-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' -// CHECK: {{^}} `-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by_or_null(function-parameter-0-0)':'void *__single' diff --git a/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr.c b/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr.c index ce19cbeb7ecc5..4e7a2dee6e354 100644 --- a/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr.c +++ b/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr.c @@ -4,18 +4,35 @@ #include #include -// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc 'void *__single __sized_by_or_null(function-parameter-0-0)(unsigned int)' // CHECK: {{^}}| |-ParmVarDecl // CHECK: {{^}}| `-AllocSizeAttr -// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc2 'void *__single __sized_by_or_null(size1)(unsigned int)' +// CHECK: {{^}}| `-ParmVarDecl +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc3 'void *__single __sized_by_or_null(size1)(unsigned int)' // CHECK: {{^}}| |-ParmVarDecl // CHECK: {{^}}| `-AllocSizeAttr +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc4 'void *__single __sized_by_or_null(size1)(unsigned int)' +// CHECK: {{^}}| `-ParmVarDecl +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc 'void *__single __sized_by_or_null(size2)(unsigned int)' +// CHECK: {{^}}| |-ParmVarDecl +// CHECK: {{^}}| `-AllocSizeAttr +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc2 'void *__single __sized_by_or_null(size2)(unsigned int)' +// CHECK: {{^}}| `-ParmVarDecl // CHECK: {{^}}|-FunctionDecl {{.+}} memcpy // CHECK: {{^}}| |-ParmVarDecl // CHECK: {{^}}| |-ParmVarDecl // CHECK: {{^}}| `-ParmVarDecl // CHECK: {{^}}| `-DependerDeclsAttr +void * myalloc3(unsigned size2) { return (void*)0; } +void * myalloc4(unsigned size2) { return (void*)0; } +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc3 'void *__single __sized_by_or_null(size2)(unsigned int)' +// CHECK: {{^}}| |-ParmVarDecl +// CHECK: {{^}}| `-AllocSizeAttr +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc4 'void *__single __sized_by_or_null(size2)(unsigned int)' +// CHECK: {{^}}| |-ParmVarDecl + void Test(unsigned siz) { // CHECK: {{^}}`-FunctionDecl [[func_Test:0x[^ ]+]] {{.+}} Test // CHECK: {{^}} |-ParmVarDecl [[var_siz:0x[^ ]+]] @@ -26,23 +43,23 @@ void Test(unsigned siz) { // CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' -// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__single __sized_by_or_null(size2)':'void *__single' // CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'unsigned int' // CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' // CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' // CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' -// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by_or_null(size2)':'void *__single' // 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: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by_or_null(size2)(*__single)(unsigned int)' // CHECK: {{^}} | | | `-DeclRefExpr {{.+}} 'myalloc' // CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' // CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' -// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by_or_null(size2)':'void *__single' void *dst = myalloc(siz); // CHECK: {{^}} |-DeclStmt @@ -50,23 +67,23 @@ void Test(unsigned siz) { // CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} // CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' -// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'void *__single __sized_by_or_null(size2)':'void *__single' // CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'unsigned int' // CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' // CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' // CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' -// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *__single __sized_by_or_null(size2)':'void *__single' // 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: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by_or_null(size2)(*__single)(unsigned int)' // CHECK: {{^}} | | | `-DeclRefExpr {{.+}} 'myalloc' // CHECK: {{^}} | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' // CHECK: {{^}} | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' -// CHECK: {{^}} | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *__single __sized_by_or_null(size2)':'void *__single' memcpy(dst, src, siz); // CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} diff --git a/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.c b/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.c index 4327311fb05b1..07d258ce468eb 100644 --- a/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.c +++ b/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.c @@ -17,15 +17,15 @@ */ // CHECK: FunctionDecl {{.+}} foo 'void (int *__single __counted_by(len), int)' -// CHECK: FunctionDecl {{.+}} foo 'void (int *__single __counted_by(len), int)' +// CHECK: FunctionDecl {{.+}} foo 'void (int *__single __terminated_by(0), int)' // CHECK: FunctionDecl {{.+}} bar 'void (int *__single __terminated_by(0), int)' // XXX: rdar://127827450 -// CHECK: FunctionDecl {{.+}} bar 'void (int *__single __terminated_by(0), int)' +// CHECK: FunctionDecl {{.+}} bar 'void (int *__single __counted_by(len), int)' void test() { - int arr[10]; - foo(arr, 10); - int *__null_terminated ptr = 0; - bar(ptr, 0); + foo(ptr, 10); + + int arr[10]; + bar(arr, 0); } diff --git a/clang/test/BoundsSafety/FixIt/fixit-function-decl.c b/clang/test/BoundsSafety/FixIt/fixit-function-decl.c index cf8275c0e97da..52a38e2316140 100644 --- a/clang/test/BoundsSafety/FixIt/fixit-function-decl.c +++ b/clang/test/BoundsSafety/FixIt/fixit-function-decl.c @@ -126,7 +126,7 @@ 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}} +// expected-error@+3 2{{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); diff --git a/clang/test/BoundsSafety/Sema/alloc-size-attr-sized-by-or-null-sys-header-override.c b/clang/test/BoundsSafety/Sema/alloc-size-attr-sized-by-or-null-sys-header-override.c new file mode 100644 index 0000000000000..8f85d43e099b5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/alloc-size-attr-sized-by-or-null-sys-header-override.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -x objective-c -fexperimental-bounds-safety-objc -verify %s + +#include +#include "alloc-size-attr-sized-by-or-null-sys-header.h" + + +__attribute__((alloc_size(1))) void * unnamed_param(unsigned size) { + return (void*)0; +} + +void * __sized_by_or_null(size) inherit_attr(unsigned size) { + return (void*)0; +} + +void * __sized_by_or_null(size) unnamed_param_inherit_attr(unsigned size) { + return (void*)0; +} + +// expected-error@+1{{invalid return type 'void *__single __sized_by_or_null(count * size)' (aka 'void *__single') for function with alloc_size attribute; '__sized_by_or_null(size * count)' or '__sized_by(size * count)' required}} +void * __sized_by_or_null(count * size) + unnamed_param_with_count_flipped(unsigned size, unsigned count) { + return (void*)0; +} + +// expected-error@+1{{'alloc_size' attribute does not match previous declaration}} +__attribute__((alloc_size(2,1))) + // expected-warning@+1 2{{omitting the parameter name in a function definition is a C23 extension}} + void * unnamed_param_with_count_mismatching_attrs(unsigned, unsigned) { + return (void*)0; +} + +// expected-warning@+1 2{{omitting the parameter name in a function definition is a C23 extension}} +void * unnamed_param_with_count_mismatching_attrs_after_param_list(unsigned, unsigned) + // expected-error@+2{{'alloc_size' attribute does not match previous declaration}} + // expected-warning@+1{{GCC does not allow 'alloc_size' attribute in this position on a function definition}} + __attribute__((alloc_size(2,1))) { + return (void*)0; +} + +void * override_nullability(unsigned, unsigned) + __attribute__((returns_nonnull)) + __attribute__((alloc_size(1, 2))); + +void * override_nullability2(unsigned, unsigned) + __attribute__((alloc_size(1, 2))); diff --git a/clang/test/BoundsSafety/Sema/alloc-size-attr-sized-by-or-null-sys-header.h b/clang/test/BoundsSafety/Sema/alloc-size-attr-sized-by-or-null-sys-header.h new file mode 100644 index 0000000000000..f4be5db16f416 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/alloc-size-attr-sized-by-or-null-sys-header.h @@ -0,0 +1,163 @@ +#pragma clang system_header + +void * __sized_by_or_null(size) explicitly_sized(int size) __attribute__((alloc_size(1))); // ok, explicit and implicit bounds align + +void * __sized_by(size) + explicitly_sized_nonnull(int size) + __attribute__((alloc_size(1))); + +void * __sized_by(size) _Nonnull explicitly_sized_nonnull_silence(int size) __attribute__((alloc_size(1))); +void * __sized_by(size) explicitly_sized_returns_nonnull_silence(int size) __attribute__((returns_nonnull)) __attribute__((alloc_size(1))); +void * __sized_by(size) explicitly_sized_returns_nonnull_after_silence(int size) __attribute__((alloc_size(1))) __attribute__((returns_nonnull)); + +void * __sized_by_or_null(size) +// expected-note@-1{{previous attribute is here}} + __sized_by(size) +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__sized_by_or_null(size)' and '__sized_by(size)'}} + _Nonnull + explicitly_sized_nonnull_redundant(int size) + __attribute__((alloc_size(1))); + +char * __sized_by(size) +// expected-note@-1{{previous attribute is here}} + _Nonnull + __counted_by(size) +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__sized_by(size)' and '__counted_by(size)'}} + explicitly_sized_nonnull_redundant2(int size) + __attribute__((alloc_size(1))); + +char * __counted_by_or_null(size) +// expected-note@-1{{previous attribute is here}} + explicitly_counted(int size) + __attribute__((alloc_size(1))); +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by_or_null(size)' and '__sized_by_or_null(size)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +char * __counted_by(size) +// expected-note@-1{{previous attribute is here}} + explicitly_counted_returns_nonnull(int size) + __attribute__((alloc_size(1))) __attribute__((returns_nonnull)); +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by(size)' and '__sized_by(size)'}} +// expected-note@-3{{function return type implicitly __sized_by because of the function's 'alloc_size' and 'returns_nonnull' attributes}} + +char * __counted_by(size) +// expected-note@-1{{previous attribute is here}} + explicitly_counted_nonnull(int size) + __attribute__((alloc_size(1))); +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by(size)' and '__sized_by_or_null(size)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + + +void * __sized_by_or_null(size * count) explicitly_sized_with_count(int size, int count) __attribute__((alloc_size(1, 2))); // ok, explicit and implicit bounds align + +void * __sized_by_or_null(count * size) +// expected-note@-1{{previous attribute is here}} + explicitly_sized_with_count_flipped(int size, int count) + __attribute__((alloc_size(1, 2))); // unfortunate, but too much complexity to support +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__sized_by_or_null(count * size)' and '__sized_by_or_null(size * count)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +void * __sized_by_or_null(size * count) +// expected-note@-1{{previous attribute is here}} + explicitly_sized_with_count_flipped2(int size, int count) + __attribute__((alloc_size(2, 1))); // unfortunate, but too much complexity to support +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__sized_by_or_null(size * count)' and '__sized_by_or_null(count * size)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +void * __sized_by(size * count) + explicitly_sized_nonnull_with_count(int size, int count) + __attribute__((alloc_size(1, 2))); + +void * __sized_by(size * count) _Nonnull explicitly_sized_nonnull_with_count_silence(int size, int count) __attribute__((alloc_size(1, 2))); +void * __sized_by(size * count) explicitly_sized_returns_nonnull_with_count_silence(int size, int count) __attribute__((returns_nonnull)) __attribute__((alloc_size(1, 2))); + +char * __counted_by_or_null(size * count) +// expected-note@-1{{previous attribute is here}} + explicitly_counted_with_count(int size, int count) + __attribute__((alloc_size(1, 2))); +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by_or_null(size * count)' and '__sized_by_or_null(size * count)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +char * __counted_by_or_null(count) +// expected-note@-1{{previous attribute is here}} + explicitly_counted_with_count2(int size, int count) + __attribute__((alloc_size(1, 2))); // no guarantee that size is 1 +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by_or_null(count)' and '__sized_by_or_null(size * count)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +char * __counted_by(size * count) +// expected-note@-1{{previous attribute is here}} + explicitly_counted_nonnull_with_count(int size, int count) + __attribute__((alloc_size(1, 2))); +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by(size * count)' and '__sized_by_or_null(size * count)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +__attribute__((alloc_size(1))) + void * __sized_by(size) + leading_attr(unsigned size) { + return (void*)0; +} + +__attribute__((alloc_size(1))) void * __sized_by(size) _Nonnull leading_attr_silence(unsigned size) { + return (void*)0; +} + +__attribute__((alloc_size(1))) void * __sized_by(size) leading_attr_silence_returns_nonnull(unsigned size) __attribute__((returns_nonnull)) { + return (void*)0; +} + +__attribute__((alloc_size(1))) + // expected-error@-1{{pointer cannot have more than one count attribute}} + // expected-note@-2{{conflicting attributes were '__counted_by(size)' and '__sized_by_or_null(size)'}} + // expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + char * __counted_by(size) + // expected-note@-1{{previous attribute is here}} + leading_attr_clash(unsigned size) { + return (void*)0; +} + +void * unnamed_param(unsigned) __attribute__((alloc_size(1))); + +void * unnamed_param_with_count(unsigned, unsigned) __attribute__((alloc_size(1, 2))); +__attribute__((alloc_size(1, 2))) void * unnamed_param_with_count(unsigned, unsigned) { + return (void*)0; +} + +void * inherit_attr(unsigned size) __attribute__((alloc_size(1))); + +void * unnamed_param_inherit_attr(unsigned) __attribute__((alloc_size(1))); + +void * unnamed_param_with_count_flipped(unsigned, unsigned) + // expected-note@+1{{attribute inherited from previous declaration here}} + __attribute__((alloc_size(1,2))); + +void * unnamed_param_with_count_mismatching_attrs(unsigned, unsigned) + // expected-note@+1{{conflicting attribute is here}} + __attribute__((alloc_size(1,2))); + +void * unnamed_param_with_count_mismatching_attrs_after_param_list(unsigned, unsigned) + // expected-note@+1{{conflicting attribute is here}} + __attribute__((alloc_size(1,2))); + +// expected-note@+1{{conflicting attribute is here}} +__attribute__((alloc_size(1, 2))) +void * clashing_attrbutes_on_same_decl(unsigned, unsigned) + // expected-error@+1{{'alloc_size' attribute does not match previous declaration}} + __attribute__((alloc_size(2, 1))); + +void * override_nullability(unsigned, unsigned) + __attribute__((alloc_size(1, 2))); + +void * override_nullability2(unsigned, unsigned) + __attribute__((returns_nonnull)) + __attribute__((alloc_size(1, 2))); diff --git a/clang/test/BoundsSafety/Sema/alloc-size-attr-sized-by-or-null.c b/clang/test/BoundsSafety/Sema/alloc-size-attr-sized-by-or-null.c new file mode 100644 index 0000000000000..26b0a056dd420 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/alloc-size-attr-sized-by-or-null.c @@ -0,0 +1,228 @@ +// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -fbounds-safety -x objective-c -fexperimental-bounds-safety-objc -verify %s + +#include + +void * __sized_by_or_null(size) explicitly_sized(int size) __attribute__((alloc_size(1))); // ok, explicit and implicit bounds align + +void * __sized_by(size) +// expected-note@-1{{previous attribute is here}} +// expected-note@-2{{add _Nonnull qualifier to return type to silence this warning}} + explicitly_sized_nonnull(int size) + __attribute__((alloc_size(1))); +// expected-warning@-1{{implicit __sized_by_or_null attribute ignored because of explicit __sized_by}} +// expected-note@-2{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +void * __sized_by(size) _Nonnull explicitly_sized_nonnull_silence(int size) __attribute__((alloc_size(1))); +void * __sized_by(size) explicitly_sized_returns_nonnull_silence(int size) __attribute__((returns_nonnull)) __attribute__((alloc_size(1))); +void * __sized_by(size) explicitly_sized_returns_nonnull_after_silence(int size) __attribute__((alloc_size(1))) __attribute__((returns_nonnull)); + +void * __sized_by_or_null(size) +// expected-note@-1{{previous attribute is here}} + __sized_by(size) +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__sized_by_or_null(size)' and '__sized_by(size)'}} + _Nonnull + explicitly_sized_nonnull_redundant(int size) + __attribute__((alloc_size(1))); + +char * __sized_by(size) +// expected-note@-1{{previous attribute is here}} + _Nonnull + __counted_by(size) +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__sized_by(size)' and '__counted_by(size)'}} + explicitly_sized_nonnull_redundant2(int size) + __attribute__((alloc_size(1))); + +char * __counted_by_or_null(size) +// expected-note@-1{{previous attribute is here}} + explicitly_counted(int size) + __attribute__((alloc_size(1))); +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by_or_null(size)' and '__sized_by_or_null(size)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +char * __counted_by(size) +// expected-note@-1{{previous attribute is here}} + explicitly_counted_returns_nonnull(int size) + __attribute__((alloc_size(1))) __attribute__((returns_nonnull)); +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by(size)' and '__sized_by(size)'}} +// expected-note@-3{{function return type implicitly __sized_by because of the function's 'alloc_size' and 'returns_nonnull' attributes}} + +char * __counted_by(size) +// expected-note@-1{{previous attribute is here}} + explicitly_counted_nonnull(int size) + __attribute__((alloc_size(1))); +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by(size)' and '__sized_by_or_null(size)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + + +void * __sized_by_or_null(size * count) explicitly_sized_with_count(int size, int count) __attribute__((alloc_size(1, 2))); // ok, explicit and implicit bounds align + +void * __sized_by_or_null(count * size) +// expected-note@-1{{previous attribute is here}} + explicitly_sized_with_count_flipped(int size, int count) + __attribute__((alloc_size(1, 2))); // unfortunate, but too much complexity to support +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__sized_by_or_null(count * size)' and '__sized_by_or_null(size * count)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +void * __sized_by_or_null(size * count) +// expected-note@-1{{previous attribute is here}} + explicitly_sized_with_count_flipped2(int size, int count) + __attribute__((alloc_size(2, 1))); // unfortunate, but too much complexity to support +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__sized_by_or_null(size * count)' and '__sized_by_or_null(count * size)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +void * __sized_by(size * count) +// expected-note@-1{{previous attribute is here}} +// expected-note@-2{{add _Nonnull qualifier to return type to silence this warning}} + explicitly_sized_nonnull_with_count(int size, int count) + __attribute__((alloc_size(1, 2))); +// expected-warning@-1{{implicit __sized_by_or_null attribute ignored because of explicit __sized_by}} +// expected-note@-2{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +void * __sized_by(size * count) _Nonnull explicitly_sized_nonnull_with_count_silence(int size, int count) __attribute__((alloc_size(1, 2))); +void * __sized_by(size * count) explicitly_sized_returns_nonnull_with_count_silence(int size, int count) __attribute__((returns_nonnull)) __attribute__((alloc_size(1, 2))); + +char * __counted_by_or_null(size * count) +// expected-note@-1{{previous attribute is here}} + explicitly_counted_with_count(int size, int count) + __attribute__((alloc_size(1, 2))); +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by_or_null(size * count)' and '__sized_by_or_null(size * count)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +char * __counted_by_or_null(count) +// expected-note@-1{{previous attribute is here}} + explicitly_counted_with_count2(int size, int count) + __attribute__((alloc_size(1, 2))); // no guarantee that size is 1 +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by_or_null(count)' and '__sized_by_or_null(size * count)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +char * __counted_by(size * count) +// expected-note@-1{{previous attribute is here}} + explicitly_counted_nonnull_with_count(int size, int count) + __attribute__((alloc_size(1, 2))); +// expected-error@-1{{pointer cannot have more than one count attribute}} +// expected-note@-2{{conflicting attributes were '__counted_by(size * count)' and '__sized_by_or_null(size * count)'}} +// expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + +__attribute__((alloc_size(1))) +// expected-warning@-1{{implicit __sized_by_or_null attribute ignored because of explicit __sized_by}} +// expected-note@-2{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + void * __sized_by(size) +// expected-note@-1{{previous attribute is here}} +// expected-note@-2{{add _Nonnull qualifier to return type to silence this warning}} + leading_attr(unsigned size) { + return (void*)0; +} + +__attribute__((alloc_size(1))) void * __sized_by(size) _Nonnull leading_attr_silence(unsigned size) { + return (void*)0; // expected-warning{{null returned from function that requires a non-null return value}} +} + +__attribute__((alloc_size(1))) void * __sized_by(size) leading_attr_silence_returns_nonnull(unsigned size) __attribute__((returns_nonnull)) { + // expected-warning@-1{{GCC does not allow 'returns_nonnull' attribute in this position on a function definition}} + return (void*)0; // expected-warning{{null returned from function that requires a non-null return value}} +} + +__attribute__((alloc_size(1))) + // expected-error@-1{{pointer cannot have more than one count attribute}} + // expected-note@-2{{conflicting attributes were '__counted_by(size)' and '__sized_by_or_null(size)'}} + // expected-note@-3{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + char * __counted_by(size) + // expected-note@-1{{previous attribute is here}} + leading_attr_clash(unsigned size) { + return (void*)0; +} + +void * unnamed_param(unsigned) __attribute__((alloc_size(1))); + +__attribute__((alloc_size(1))) void * unnamed_param(unsigned size) { + return (void*)0; +} + +void * unnamed_param_with_count(unsigned, unsigned) __attribute__((alloc_size(1, 2))); +__attribute__((alloc_size(1, 2))) void * unnamed_param_with_count(unsigned, unsigned) { + // expected-warning@-1 2{{omitting the parameter name in a function definition is a C23 extension}} + return (void*)0; +} + +void * inherit_attr(unsigned size) __attribute__((alloc_size(1))); +void * __sized_by_or_null(size) inherit_attr(unsigned size) { + return (void*)0; +} + +void * unnamed_param_inherit_attr(unsigned) __attribute__((alloc_size(1))); +void * __sized_by_or_null(size) unnamed_param_inherit_attr(unsigned size) { + return (void*)0; +} + +void * unnamed_param_with_count_flipped(unsigned, unsigned) + // expected-note@-1{{previous declaration is here}} + __attribute__((alloc_size(1,2))); + // expected-note@-1{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} +void * __sized_by_or_null(count * size) + // expected-error@-1{{conflicting '__sized_by_or_null' attribute with the previous function declaration}} + // expected-note@-2{{conflicting attributes were '__sized_by_or_null(function-parameter-0-0 * function-parameter-0-1)' and '__sized_by_or_null(count * size)'}} + unnamed_param_with_count_flipped(unsigned size, unsigned count) { + return (void*)0; +} + +void * unnamed_param_with_count_mismatching_attrs(unsigned, unsigned) + __attribute__((alloc_size(1,2))); + // expected-note@-1{{conflicting attribute is here}} +__attribute__((alloc_size(2,1))) + // expected-error@-1{{'alloc_size' attribute does not match previous declaration}} + void * unnamed_param_with_count_mismatching_attrs(unsigned, unsigned) { + // expected-warning@-1 2{{omitting the parameter name in a function definition is a C23 extension}} + return (void*)0; +} + +void * unnamed_param_with_count_mismatching_attrs_after_param_list(unsigned, unsigned) + __attribute__((alloc_size(1,2))); + // expected-note@-1{{conflicting attribute is here}} +void * unnamed_param_with_count_mismatching_attrs_after_param_list(unsigned, unsigned) + // expected-warning@-1 2{{omitting the parameter name in a function definition is a C23 extension}} + __attribute__((alloc_size(2,1))) { + // expected-error@-1{{'alloc_size' attribute does not match previous declaration}} + // expected-warning@-2{{GCC does not allow 'alloc_size' attribute in this position on a function definition}} + return (void*)0; +} + +__attribute__((alloc_size(1, 2))) +// expected-note@-1{{conflicting attribute is here}} +void * clashing_attrbutes_on_same_decl(unsigned, unsigned) + __attribute__((alloc_size(2, 1))); + // expected-error@-1{{'alloc_size' attribute does not match previous declaration}} + +// expected-note@+1{{previous declaration is here}} +void * override_nullability(unsigned, unsigned) + // expected-note@+1{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + __attribute__((alloc_size(1, 2))); +// expected-note@+2{{conflicting attributes were '__sized_by_or_null(function-parameter-0-0 * function-parameter-0-1)' and '__sized_by(function-parameter-0-0 * function-parameter-0-1)'}} +// expected-error@+1{{conflicting '__sized_by' attribute with the previous function declaration}} +void * override_nullability(unsigned, unsigned) + __attribute__((returns_nonnull)) + // expected-note@+1{{function return type implicitly __sized_by because of the function's 'alloc_size' and 'returns_nonnull' attributes}} + __attribute__((alloc_size(1, 2))); + +// expected-note@+1{{previous declaration is here}} +void * override_nullability2(unsigned, unsigned) + __attribute__((returns_nonnull)) + // expected-note@+1{{function return type implicitly __sized_by because of the function's 'alloc_size' and 'returns_nonnull' attributes}} + __attribute__((alloc_size(1, 2))); +// expected-note@+2{{conflicting attributes were '__sized_by(function-parameter-0-0 * function-parameter-0-1)' and '__sized_by_or_null(function-parameter-0-0 * function-parameter-0-1)'}} +// expected-error@+1{{conflicting '__sized_by_or_null' attribute with the previous function declaration}} +void * override_nullability2(unsigned, unsigned) + // expected-note@+1{{function return type implicitly __sized_by_or_null because of the function's 'alloc_size' attribute}} + __attribute__((alloc_size(1, 2))); + +// expected-error@+1{{cannot combine '__sized_by_or_null' and 'returns_nonnull'; did you mean '__sized_by' instead?}} +void * __sized_by_or_null(x * y) ignore_implicit_type_reverse_nullability(unsigned x, unsigned y) __attribute__((returns_nonnull)) __attribute__((alloc_size(1,2))); diff --git a/clang/test/BoundsSafety/Sema/count-bound-attrs.c b/clang/test/BoundsSafety/Sema/count-bound-attrs.c index 9b3b5e9362528..c3f59f8bd6cb1 100644 --- a/clang/test/BoundsSafety/Sema/count-bound-attrs.c +++ b/clang/test/BoundsSafety/Sema/count-bound-attrs.c @@ -17,6 +17,8 @@ void Test(void) { 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}} + // expected-note@-1{{conflicting attributes were '__counted_by(len7)' and '__counted_by(len7 + 1)'}} + // expected-note@-2{{previous attribute is here}} int len8; int *__counted_by(len8) __single countThin; diff --git a/clang/test/BoundsSafety/Sema/counted_by_semantic_checks.c b/clang/test/BoundsSafety/Sema/counted_by_semantic_checks.c index 0677e74116101..8720014f9ddbf 100644 --- a/clang/test/BoundsSafety/Sema/counted_by_semantic_checks.c +++ b/clang/test/BoundsSafety/Sema/counted_by_semantic_checks.c @@ -7,6 +7,8 @@ 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}} + // expected-note@-1{{conflicting attributes were '__counted_by(len)' and '__counted_by(len + 1)'}} + // expected-note@-2{{previous attribute is here}} 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}} diff --git a/clang/test/BoundsSafety/Sema/ptrs-with-multiple-range-attrs.c b/clang/test/BoundsSafety/Sema/ptrs-with-multiple-range-attrs.c index 09b97c27e33d1..d23b17e07910d 100644 --- a/clang/test/BoundsSafety/Sema/ptrs-with-multiple-range-attrs.c +++ b/clang/test/BoundsSafety/Sema/ptrs-with-multiple-range-attrs.c @@ -1,15 +1,21 @@ -// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fexperimental-bounds-safety-objc -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 +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,c %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fexperimental-bounds-safety-objc -verify=expected,c %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify=expected,c %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify=expected,cxx %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify=expected,c %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify=expected,cxx %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}} + // expected-note@-1{{conflicting attributes were '__counted_by(len)' and '__counted_by(len + 2)'}} + // expected-note@-2{{previous attribute is here}} void bar(int **__counted_by(len) buf __counted_by(len + 1), int len); // expected-error{{pointer cannot have more than one count attribute}} + // expected-note@-1{{conflicting attributes were '__counted_by(len + 1)' and '__counted_by(len)'}} + // expected-note@-2{{previous attribute is here}} int *__counted_by(len) __counted_by(len + 2) baz(int len); // expected-error{{pointer cannot have more than one count attribute}} + // expected-note@-1{{conflicting attributes were '__counted_by(len)' and '__counted_by(len + 2)'}} + // expected-note@-2{{previous attribute is here}} int *__counted_by(len) bazx(int len); int *__counted_by(len) bazx(int len); // ok - same count expr @@ -24,12 +30,12 @@ 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}} +// expected-note@+1 2{{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}} +// expected-error@+2{{conflicting '__sized_by' attribute with the previous function declaration}} +// expected-note@+1{{conflicting attributes were '__counted_by(len)' and '__sized_by(len)'}} int *__sized_by(len) bayz(unsigned len) {} // expected-note@+1{{previous declaration is here}} @@ -45,12 +51,15 @@ 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}} +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +// expected-note@+1{{conflicting attributes were '__sized_by(len)' and '__counted_by(4)'}} 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}} + // expected-note@-1{{conflicting attributes were '__counted_by(len)' and '__counted_by(cnt)'}} + // expected-note@-2{{previous attribute is here}} void corge(int *__counted_by(len) ptr, int len); void corge(int *__counted_by(len) ptr, int len); // ok @@ -66,7 +75,12 @@ int ** grault2(int len); 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 *__counted_by(l) // expected-error{{pointer cannot have more than one count attribute}} + // c-note@-1{{conflicting attributes were '__counted_by(l + 1)' and '__counted_by(l)'}} + // cxx-note@-2{{conflicting attributes were '__counted_by(l + 1)' and '__counted_by(this->l)'}} rdar://139834323 (Struct field in count expression is printed inconsistently in C++ mode) + bp2 + __counted_by(l+1); // c-note{{previous attribute is here}} + // cxx-note@+1{{previous attribute is here}} rdar://139834115 (Count expression has incorrect location in C++ mode) int l; }; @@ -80,13 +94,11 @@ struct T { 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}} +// expected-note@+1 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 *a, int *__ended_by(a) b); // expected-error 2{{conflicting '__ended_by' attribute with the previous function declaration}} mismatch on both a and b 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 diff --git a/clang/test/BoundsSafety/Sema/redundant-attrs.c b/clang/test/BoundsSafety/Sema/redundant-attrs.c index 47f5ddd204392..55833579b39d4 100644 --- a/clang/test/BoundsSafety/Sema/redundant-attrs.c +++ b/clang/test/BoundsSafety/Sema/redundant-attrs.c @@ -41,21 +41,29 @@ char * __null_terminated __null_terminated ptrBoundBound5; // expected-warning{{ int len = 0; int * __counted_by(len) __counted_by(len) ptrBoundBound6; // expected-error{{pointer cannot have more than one count attribute}} + // expected-note@-1{{conflicting attributes were '__counted_by(len)' and '__counted_by(len)'}} + // expected-note@-2{{previous attribute is here}} struct S1 { int size; int arrBoundBound[__counted_by(size) __counted_by(size)]; // expected-error{{array cannot have more than one count attribute}} + // expected-note@-1{{conflicting attributes were '__counted_by(size)' and '__counted_by(size)'}} + // expected-note@-2{{previous attribute is here}} }; struct S2 { int size; int len; int arrBoundBound[__counted_by(size) __counted_by(len)]; // expected-error{{array cannot have more than one count attribute}} + // expected-note@-1{{conflicting attributes were '__counted_by(size)' and '__counted_by(len)'}} + // expected-note@-2{{previous attribute is here}} }; #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}} + // expected-note@-1{{conflicting attributes were '__counted_by(len)' and '__counted_by(len)'}} + // expected-note@-2{{previous attribute is here}} 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}} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-redecl.c b/clang/test/BoundsSafety/Sema/terminated-by-redecl.c index 0a932dda946fe..b5911cb7618a4 100644 --- a/clang/test/BoundsSafety/Sema/terminated-by-redecl.c +++ b/clang/test/BoundsSafety/Sema/terminated-by-redecl.c @@ -11,7 +11,8 @@ 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}} +// expected-error@+2{{conflicting '__terminated_by' attribute with the previous function declaration}} +// expected-note@+1{{conflicting arguments for terminator were '0' and '-1'}} const char *__terminated_by(-1) test2(); const char *test3(); @@ -24,7 +25,8 @@ 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}} +// expected-error@+2{{conflicting '__terminated_by' attribute with the previous function declaration}} +// expected-note@+1{{conflicting arguments for terminator were '0' and '-1'}} void test5(int *__terminated_by(-1) arg); // expected-note@+1{{previous declaration is here}} @@ -44,7 +46,8 @@ 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}} +// expected-error@+2{{conflicting '__ended_by' attribute with the previous function declaration}} +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} void test9(int *buf, int *__null_terminated end); #include "terminated-by-redecl.h" diff --git a/clang/test/Sema/internal_linkage.c b/clang/test/Sema/internal_linkage.c index a1bff73fb6620..4ea8599039c14 100644 --- a/clang/test/Sema/internal_linkage.c +++ b/clang/test/Sema/internal_linkage.c @@ -20,7 +20,7 @@ struct __attribute__((internal_linkage)) S { // expected-warning{{'internal_link __attribute__((internal_linkage("foo"))) int g(void) {} // expected-error{{'internal_linkage' attribute takes no arguments}} int var6 [[clang::internal_linkage]]; -int var7 [[clang::internal_linkage]] __attribute__((common)); // expected-error{{'clang::internal_linkage' and 'common' attributes are not compatible}} \ +int var7 [[clang::internal_linkage]] __attribute__((common)); // expected-error{{'common' and 'clang::internal_linkage' attributes are not compatible}} \ // expected-note{{conflicting attribute is here}} __attribute__((common)) int var8 [[clang::internal_linkage]]; // expected-error{{'clang::internal_linkage' and 'common' attributes are not compatible}} \ // expected-note{{conflicting attribute is here}} diff --git a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp index 17078e2bc1505..7219f0cf936a9 100644 --- a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp +++ b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp @@ -421,6 +421,7 @@ enum ModifierType { MT_ObjCClass, MT_ObjCInstance, MT_Quoted, + MT_Unquoted, }; static StringRef getModifierName(ModifierType MT) { @@ -450,6 +451,8 @@ static StringRef getModifierName(ModifierType MT) { return "objcinstance"; case MT_Quoted: return "quoted"; + case MT_Unquoted: + return "unquoted"; case MT_Unknown: llvm_unreachable("invalid modifier type"); } @@ -1117,6 +1120,7 @@ Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text, .Case("objcclass", MT_ObjCClass) .Case("objcinstance", MT_ObjCInstance) .Case("quoted", MT_Quoted) + .Case("unquoted", MT_Unquoted) .Case("", MT_Placeholder) .Default(MT_Unknown); @@ -1269,6 +1273,7 @@ Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text, case MT_ObjCClass: case MT_ObjCInstance: case MT_Quoted: + case MT_Unquoted: case MT_Ordinal: case MT_Human: { Parsed.push_back(New(ModType, parseModifier(Text)));