https://github.com/delcypher updated https://github.com/llvm/llvm-project/pull/106321
>From b17b93dc917077cb588befde683a621e096c4f85 Mon Sep 17 00:00:00 2001 From: Dan Liew <d...@su-root.co.uk> Date: Thu, 15 Aug 2024 16:33:04 -0700 Subject: [PATCH] [BoundsSafety][Sema] Allow counted_by and counted_by_or_null on pointers where the pointee type is incomplete but potentially completable Previously using the `counted_by` or `counted_by_or_null` attribute on a pointer with an incomplete pointee type was forbidden. Unfortunately this prevented a situation like the following from being allowed. Header file: ``` struct EltTy; // Incomplete type struct Buffer { size_t count; struct EltTy* __counted_by(count) buffer; // error before this patch }; ``` Implementation file: ``` struct EltTy { // definition }; void allocBuffer(struct Buffer* b) { b->buffer = malloc(sizeof(EltTy)* b->count); } ``` To allow code like the above but still enforce that the pointee type size is known in locations where `-fbounds-safety` needs to emit bounds checks the following scheme is used. * For incomplete pointee types that can never be completed (e.g. `void`) these are treated as error where the attribute is written (just like before this patch). * For incomplete pointee types that might be completable later on (struct, union, and enum forward declarations) in the translation unit, writing the attribute on the incomplete pointee type is allowed on the FieldDecl declaration but "uses" of the declared pointer are forbidden if at the point of "use" the pointee type is still incomplete. For this patch a "use" of a FieldDecl covers: * Explicit and Implicit initialization (note see **Tentative Definition Initialization** for an exception to this) * Assignment * Conversion to an lvalue (e.g. for use in an expression) In the swift lang fork of Clang the `counted_by` and `counted_by_or_null` attribute are allowed in many more contexts. That isn't the case for upstream Clang so the "use" checks for the attribute on VarDecl, ParamVarDecl, and function return type have been omitted from this patch because they can't be tested. However, the `BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy` and `BoundsSafetyCheckUseOfCountAttrPtrWithIncompletePointeeTy` functions retain the ability to emit diagnostics for these other contexts to avoid unnecessary divergence between upstream Clang and Apple's internal fork. Support for checking "uses" will be upstreamed when upstream Clang allows the `counted_by` and `counted_by_or_null` attribute in additional contexts. This patch has a few limitations: ** 1. Tentative Defition Initialization ** This patch currently allows something like: ``` struct IncompleteTy; struct Buffer { int count; struct IncompleteTy* __counted_by(count) buf; }; // Tentative definition struct Buffer GlobalBuf; ``` The Tentative definition in this example becomes an actual definition whose initialization **should be checked** but it currently isn't. Addressing this problem will be done in a subseqent patch. ** 2. When the incomplete pointee type is a typedef diagnostics are slightly misleading ** For this situation: ``` struct IncompleteTy; typedef struct IncompleteTy Incomplete_t; struct Buffer { int count; struct IncompleteTy* __counted_by(count) buf; }; void use(struct Buffer b) { b.buf = 0x0; } ``` This code emits `note: forward declaration of 'Incomplete_t' (aka 'struct IncompleteTy')` but the location is on the `struct IncompleteTy;` forward declaration. This is misleading because `Incomplete_t` isn't actually forward declared there (instead the underlying type is). This could be resolved by additional diagnostics that walk the chain of typedefs and explain each step of the walk. However, that would be very verbose and didn't seem like a direction worth pursuing. rdar://133600117 --- clang/include/clang/AST/Type.h | 22 + .../clang/Basic/DiagnosticSemaKinds.td | 37 ++ clang/include/clang/Sema/Sema.h | 44 ++ clang/lib/AST/Type.cpp | 41 ++ clang/lib/Sema/SemaBoundsSafety.cpp | 213 +++++- clang/lib/Sema/SemaExpr.cpp | 23 + clang/lib/Sema/SemaInit.cpp | 13 + .../attr-counted-by-late-parsed-struct-ptrs.c | 5 +- .../Sema/attr-counted-by-or-null-last-field.c | 4 +- ...unted-by-or-null-late-parsed-struct-ptrs.c | 5 +- .../attr-counted-by-or-null-struct-ptrs.c | 12 +- ...ruct-ptrs-completable-incomplete-pointee.c | 622 ++++++++++++++++++ clang/test/Sema/attr-counted-by-struct-ptrs.c | 12 +- clang/test/Sema/attr-counted-by-vla.c | 3 +- 14 files changed, 1029 insertions(+), 27 deletions(-) create mode 100644 clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 9f6189440fabf..0d96262beb785 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2433,6 +2433,26 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { return !isFunctionType(); } + /// \returns True if the type is incomplete and it is also a type that + /// cannot be completed by a later type definition. + /// + /// E.g. For `void` this is true but for `struct ForwardDecl;` this is false + /// because a definition for `ForwardDecl` could be provided later on in the + /// translation unit. + /// + /// Note even for types that this function returns true for it is still + /// possible for the declarations that contain this type to later have a + /// complete type in a translation unit. E.g.: + /// + /// \code{.c} + /// // This decl has type 'char[]' which is incomplete and cannot be later + /// // completed by another by another type declaration. + /// extern char foo[]; + /// // This decl now has complete type 'char[5]'. + /// char foo[5]; // foo has a complete type + /// \endcode + bool isAlwaysIncompleteType() const; + /// Determine whether this type is an object type. bool isObjectType() const { // C++ [basic.types]p8: @@ -3349,6 +3369,8 @@ class CountAttributedType final static bool classof(const Type *T) { return T->getTypeClass() == CountAttributed; } + + StringRef getAttributeName(bool WithMacroPrefix) const; }; /// Represents a type which was implicitly adjusted by the semantic diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1ad09aba60935..b7425431f18f3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4445,6 +4445,7 @@ def err_mismatched_visibility: Error<"visibility does not match previous declara def note_previous_attribute : Note<"previous attribute is here">; def note_conflicting_attribute : Note<"conflicting attribute is here">; def note_attribute : Note<"attribute is here">; +def note_named_attribute : Note<"%0 attribute is here">; def err_mismatched_ms_inheritance : Error< "inheritance model does not match %select{definition|previous declaration}0">; def warn_ignored_ms_inheritance : Warning< @@ -6710,6 +6711,42 @@ def err_counted_by_attr_pointee_unknown_size : Error< "%select{|. This will be an error in a future compiler version}3" "" "}2">; +def err_counted_by_on_incomplete_type_on_assign : Error < + "cannot %select{" + "assign to %select{object|'%1'}2 that has|" // AA_Assigning, + "pass argument to %select{parameter|parameter '%1'}2 that has|" // AA_Passing, + "return|" // AA_Returning, + "convert to|" // AA_Converting (UNUSED) + "%select{|implicitly }3initialize %select{object|'%1'}2 that has|" // AA_Initializing, + "pass argument to parameter that has|" // AA_Sending (UNUSED) + "cast to|" // AA_Casting (UNUSED) + "pass argument to parameter that has" // AA_Passing_CFAudited (UNUSED) + "}0 type %4 because the pointee type %6 is incomplete and the '%5' attribute " + "requires the pointee type be complete when %select{" + "assigning|" // AA_Assigning, + "passing|" // AA_Passing, + "returning|" // AA_Returning, + "converting|" // AA_Converting (UNUSED) + "initializing|" // AA_Initializing, + "passing|" // AA_Sending (UNUSED) + "casting|" // AA_Casting (UNUSED) + "passing" // AA_Passing_CFAudited (UNUSED) + "}0; " + "consider providing a complete definition for %6 or using the " + "'__sized_by%select{|_or_null}7' attribute" +>; +def err_counted_by_on_incomplete_type_on_use : Error < + "cannot %select{" + "use '%1'|" // Generic expr + "call '%1'" // CallExpr + "}0 with%select{" + "|" // Generic expr + " return" // CallExpr + "}0 type %2 because the pointee type %3 is incomplete " + "and the '%4' attribute requires the pointee type be complete in this context; " + "consider providing a complete definition for %3 or using the " + "'__sized_by%select{|_or_null}5' attribute" +>; def warn_counted_by_attr_elt_type_unknown_size : Warning<err_counted_by_attr_pointee_unknown_size.Summary>, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7bd77d33a1f3d..59659960da163 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2098,6 +2098,50 @@ class Sema final : public SemaBase { bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, bool OrNull); + /// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or + /// `__counted_by_or_null` pointer type \param LHSTy. + /// + /// \param LHSTy The type being assigned to. Checks will only be performed if + /// the type is a `counted_by` or `counted_by_or_null ` pointer. + /// \param RHSExpr The expression being assigned from. + /// \param Action The type assignment being performed + /// \param Loc The SourceLocation to use for error diagnostics + /// \param Assignee The ValueDecl being assigned. This is used to compute + /// the name of the assignee. If the assignee isn't known this can + /// be set to nullptr. + /// \param ShowFullyQualifiedAssigneeName If set to true when using \p + /// Assignee to compute the name of the assignee use the fully + /// qualified name, otherwise use the unqualified name. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckAssignmentToCountAttrPtr( + QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, + SourceLocation Loc, const ValueDecl *Assignee, + bool ShowFullyQualifiedAssigneeName); + + /// Perform Bounds Safety Semantic checks for initializing a Bounds Safety + /// pointer. + /// + /// \param Entity The entity being initialized + /// \param Kind The kind of initialization being performed + /// \param Action The type assignment being performed + /// \param LHSTy The type being assigned to. Checks will only be performed if + /// the type is a `counted_by` or `counted_by_or_null ` pointer. + /// \param RHSExpr The expression being used for initialization. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckInitialization(const InitializedEntity &Entity, + const InitializationKind &Kind, + AssignmentAction Action, + QualType LHSType, Expr *RHSExpr); + + /// Perform Bounds Safety semantic checks for uses of invalid uses counted_by + /// or counted_by_or_null pointers in \param E. + /// + /// \param E the expression to check + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E); ///@} // diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 4336fe44b82ad..1dbad2e55191f 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2490,6 +2490,22 @@ bool Type::isIncompleteType(NamedDecl **Def) const { } } +bool Type::isAlwaysIncompleteType() const { + if (!isIncompleteType()) + return false; + + // Forward declarations of structs, classes, enums, and unions could be later + // completed in a compilation unit by providing a type definition. + if (getAsTagDecl()) + return false; + + // Other types are incompletable. + // + // E.g. `char[]` and `void`. The type is incomplete and no future + // type declarations can make the type complete. + return true; +} + bool Type::isSizelessBuiltinType() const { if (isSizelessVectorType()) return true; @@ -3931,6 +3947,31 @@ CountAttributedType::CountAttributedType( DeclSlot[i] = CoupledDecls[i]; } +StringRef CountAttributedType::getAttributeName(bool WithMacroPrefix) const { +// TODO: This method isn't really ideal because it doesn't return the spelling +// of the attribute that was used in the user's code. This method is used for +// diagnostics so the fact it doesn't use the spelling of the attribute in +// the user's code could be confusing (#113585). +#define ENUMERATE_ATTRS(PREFIX) \ + do { \ + if (isCountInBytes()) { \ + if (isOrNull()) \ + return PREFIX "sized_by_or_null"; \ + return PREFIX "sized_by"; \ + } \ + if (isOrNull()) \ + return PREFIX "counted_by_or_null"; \ + return PREFIX "counted_by"; \ + } while (0) + + if (WithMacroPrefix) + ENUMERATE_ATTRS("__"); + else + ENUMERATE_ATTRS(""); + +#undef ENUMERATE_ATTRS +} + TypedefType::TypedefType(TypeClass tc, const TypedefNameDecl *D, QualType Underlying, QualType can) : Type(tc, can, toSemanticDependence(can->getDependence())), diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp index d63a2389ea11d..f3aa063cbdf49 100644 --- a/clang/lib/Sema/SemaBoundsSafety.cpp +++ b/clang/lib/Sema/SemaBoundsSafety.cpp @@ -11,6 +11,9 @@ /// (e.g. `counted_by`) /// //===----------------------------------------------------------------------===// +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/Initialization.h" #include "clang/Sema/Sema.h" namespace clang { @@ -102,7 +105,36 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, // only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable // when `FieldTy->isArrayType()`. bool ShouldWarn = false; - if (PointeeTy->isIncompleteType() && !CountInBytes) { + if (PointeeTy->isAlwaysIncompleteType() && !CountInBytes) { + // In general using `counted_by` or `counted_by_or_null` on + // pointers where the pointee is an incomplete type are problematic. This is + // because it isn't possible to compute the pointer's bounds without knowing + // the pointee type size. At the same time it is common to forward declare + // types in header files. + // + // E.g.: + // + // struct Handle; + // struct Wrapper { + // size_t size; + // struct Handle* __counted_by(count) handles; + // } + // + // To allow the above code pattern but still prevent the pointee type from + // being incomplete in places where bounds checks are needed the following + // scheme is used: + // + // * When the pointee type might not always be an incomplete type (i.e. + // a type that is currently incomplete but might be completed later + // on in the translation unit) the attribute is allowed by this method + // but later uses of the FieldDecl are checked that the pointee type + // is complete see `BoundsSafetyCheckAssignmentToCountAttrPtr`, + // `BoundsSafetyCheckInitialization`, and + // `BoundsSafetyCheckUseOfCountAttrPtr` + // + // * When the pointee type is always an incomplete type (e.g. + // `void`) the attribute is disallowed by this method because we know the + // type can never be completed so there's no reason to allow it. InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE; } else if (PointeeTy->isSizelessType()) { InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS; @@ -186,4 +218,183 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, return false; } +static void EmitIncompleteCountedByPointeeNotes(Sema &S, + const CountAttributedType *CATy, + NamedDecl *IncompleteTyDecl, + bool NoteAttrLocation = true) { + assert(IncompleteTyDecl == nullptr || isa<TypeDecl>(IncompleteTyDecl)); + + if (NoteAttrLocation) { + // Note where the attribute is declared + // This is an approximation that's not quite right. This points to the + // the expression inside the attribute rather than the attribute itself. + // + // TODO: Implement logic to find the relevant TypeLoc for the attribute and + // get the SourceRange from that (#113582). + SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange(); + S.Diag(AttrSrcRange.getBegin(), diag::note_named_attribute) + << CATy->getAttributeName(/*WithMacroPrefix=*/true) << AttrSrcRange; + } + + if (!IncompleteTyDecl) + return; + + // If there's an associated forward declaration display it to emphasize + // why the type is incomplete (all we have is a forward declaration). + + // Note the `IncompleteTyDecl` type is the underlying type which might not + // be the same as `CATy->getPointeeType()` which could be a typedef. + // + // The diagnostic printed will be at the location of the underlying type but + // the diagnostic text will print the type of `CATy->getPointeeType()` which + // could be a typedef name rather than the underlying type. This is ok + // though because the diagnostic will print the underlying type name too. + // E.g: + // + // `forward declaration of 'Incomplete_Struct_t' + // (aka 'struct IncompleteStructTy')` + S.Diag(IncompleteTyDecl->getBeginLoc(), diag::note_forward_declaration) + << CATy->getPointeeType(); +} + +static std::tuple<const CountAttributedType *, QualType> +GetCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND) { + auto *CATy = Ty->getAs<CountAttributedType>(); + // Incomplete pointee type is only a problem for + // counted_by/counted_by_or_null + if (!CATy || CATy->isCountInBytes()) + return {}; + + auto PointeeTy = CATy->getPointeeType(); + if (PointeeTy.isNull()) { + // Reachable if `CountAttributedType` wraps an IncompleteArrayType + return {}; + } + + if (!PointeeTy->isIncompleteType(ND)) + return {}; + + return {CATy, PointeeTy}; +} + +/// Perform Checks for assigning to a `__counted_by` or +/// `__counted_by_or_null` pointer type \param LHSTy where the pointee type +/// is incomplete which is invalid. +/// +/// \param S The Sema instance. +/// \param LHSTy The type being assigned to. Checks will only be performed if +/// the type is a `counted_by` or `counted_by_or_null ` pointer. +/// \param RHSExpr The expression being assigned from. +/// \param Action The type assignment being performed +/// \param Loc The SourceLocation to use for error diagnostics +/// \param Assignee The ValueDecl being assigned. This is used to compute +/// the name of the assignee. If the assignee isn't known this can +/// be set to nullptr. +/// \param ShowFullyQualifiedAssigneeName If set to true when using \p +/// Assignee to compute the name of the assignee use the fully +/// qualified name, otherwise use the unqualified name. +/// +/// \returns True iff no diagnostic where emitted, false otherwise. +static bool CheckAssignmentToCountAttrPtrWithIncompletePointeeTy( + Sema &S, QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, + SourceLocation Loc, const ValueDecl *Assignee, + bool ShowFullyQualifiedAssigneeName) { + NamedDecl *IncompleteTyDecl = nullptr; + auto [CATy, PointeeTy] = + GetCountedByAttrOnIncompletePointee(LHSTy, &IncompleteTyDecl); + if (!CATy) + return true; + + std::string AssigneeStr; + if (Assignee) { + if (ShowFullyQualifiedAssigneeName) { + AssigneeStr = Assignee->getQualifiedNameAsString(); + } else { + AssigneeStr = Assignee->getNameAsString(); + } + } + + S.Diag(Loc, diag::err_counted_by_on_incomplete_type_on_assign) + << static_cast<int>(Action) << AssigneeStr << (AssigneeStr.size() > 0) + << isa<ImplicitValueInitExpr>(RHSExpr) << LHSTy + << CATy->getAttributeName(/*WithMacroPrefix=*/true) << PointeeTy + << CATy->isOrNull() << RHSExpr->getSourceRange(); + + EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl); + return false; // check failed +} + +bool Sema::BoundsSafetyCheckAssignmentToCountAttrPtr( + QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, SourceLocation Loc, + const ValueDecl *Assignee, bool ShowFullyQualifiedAssigneeName) { + return CheckAssignmentToCountAttrPtrWithIncompletePointeeTy( + *this, LHSTy, RHSExpr, Action, Loc, Assignee, + ShowFullyQualifiedAssigneeName); +} + +bool Sema::BoundsSafetyCheckInitialization(const InitializedEntity &Entity, + const InitializationKind &Kind, + AssignmentAction Action, + QualType LHSType, Expr *RHSExpr) { + auto SL = Kind.getLocation(); + + // Note: We don't call `BoundsSafetyCheckAssignmentToCountAttrPtr` here + // because we need conditionalize what is checked. In downstream + // Clang `counted_by` is supported on variable definitions and in that + // implementation an error diagnostic will be emitted on the variable + // definition if the pointee is an incomplete type. To avoid warning about the + // same problem twice (once when the variable is defined, once when Sema + // checks the initializer) we skip checking the initializer if it's a + // variable. + if (Action == AssignmentAction::Initializing && + Entity.getKind() != InitializedEntity::EK_Variable) { + + if (!CheckAssignmentToCountAttrPtrWithIncompletePointeeTy( + *this, LHSType, RHSExpr, Action, SL, + dyn_cast_or_null<ValueDecl>(Entity.getDecl()), + /*ShowFullQualifiedAssigneeName=*/true)) { + return false; + } + } + + return true; +} + +bool Sema::BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E) { + QualType T = E->getType(); + if (!T->isPointerType()) + return true; + + NamedDecl *IncompleteTyDecl = nullptr; + auto [CATy, PointeeTy] = + GetCountedByAttrOnIncompletePointee(T, &IncompleteTyDecl); + if (!CATy) + return true; + + // Generate a string for the diagnostic that describes the "use". + // The string is specialized for direct calls to produce a better + // diagnostic. + SmallString<64> UseStr; + bool IsDirectCall = false; + if (const auto *CE = dyn_cast<CallExpr>(E->IgnoreParens())) { + if (const auto *FD = CE->getDirectCallee()) { + UseStr = FD->getName(); + IsDirectCall = true; + } + } + + if (!IsDirectCall) { + llvm::raw_svector_ostream SS(UseStr); + E->printPretty(SS, nullptr, getPrintingPolicy()); + } + + Diag(E->getBeginLoc(), diag::err_counted_by_on_incomplete_type_on_use) + << IsDirectCall << UseStr << T << PointeeTy + << CATy->getAttributeName(/*WithMacroPrefix=*/true) << CATy->isOrNull() + << E->getSourceRange(); + + EmitIncompleteCountedByPointeeNotes(*this, CATy, IncompleteTyDecl); + return false; +} + } // namespace clang diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index e7f418ae6802e..13f3c7f377b50 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -732,6 +732,9 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) Cleanup.setExprNeedsCleanups(true); + if (!BoundsSafetyCheckUseOfCountAttrPtr(Res.get())) + return ExprError(); + // C++ [conv.lval]p3: // If T is cv std::nullptr_t, the result is a null pointer constant. CastKind CK = T->isNullPtrType() ? CK_NullToPointer : CK_LValueToRValue; @@ -13806,6 +13809,26 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS, QualType LHSType = LHSExpr->getType(); QualType RHSType = CompoundType.isNull() ? RHS.get()->getType() : CompoundType; + + if (RHS.isUsable()) { + // Even if this check fails don't return early to allow the best + // possible error recovery and to allow any subsequent diagnostics to + // work. + const ValueDecl *Assignee = nullptr; + bool ShowFullyQualifiedAssigneeName = false; + // In simple cases describe what is being assigned to + if (auto *DR = dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenCasts())) { + Assignee = DR->getDecl(); + } else if (auto *ME = dyn_cast<MemberExpr>(LHSExpr->IgnoreParenCasts())) { + Assignee = ME->getMemberDecl(); + ShowFullyQualifiedAssigneeName = true; + } + + BoundsSafetyCheckAssignmentToCountAttrPtr( + LHSType, RHS.get(), AssignmentAction::Assigning, Loc, Assignee, + ShowFullyQualifiedAssigneeName); + } + // OpenCL v1.2 s6.1.1.1 p2: // The half data type can only be used to declare a pointer to a buffer that // contains half values diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 87a4244f2fb76..dba9c28416b63 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -8314,6 +8314,12 @@ ExprResult InitializationSequence::Perform(Sema &S, Kind.getRange().getEnd()); } else { CurInit = new (S.Context) ImplicitValueInitExpr(Step->Type); + // Note the return value isn't used to return a ExprError() when + // initialization fails . For struct initialization allows all field + // assignments to be checked rather than bailing on the first error. + S.BoundsSafetyCheckInitialization(Entity, Kind, + AssignmentAction::Initializing, + Step->Type, CurInit.get()); } break; } @@ -8360,6 +8366,13 @@ ExprResult InitializationSequence::Perform(Sema &S, } } + // Note the return value isn't used to return a ExprError() when + // initialization fails. For struct initialization this allows all field + // assignments to be checked rather than bailing on the first error. + S.BoundsSafetyCheckInitialization(Entity, Kind, + getAssignmentAction(Entity, true), + Step->Type, InitialCurInit.get()); + bool Complained; if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(), Step->Type, SourceType, diff --git a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c index 9ff3b080f6576..8d4e0c510603a 100644 --- a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c +++ b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c @@ -19,13 +19,12 @@ struct on_member_pointer_complete_ty { }; struct on_member_pointer_incomplete_ty { - struct size_unknown * buf __counted_by(count); // expected-error{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}} + struct size_unknown * buf __counted_by(count); // ok int count; }; struct on_member_pointer_const_incomplete_ty { - // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}} - const struct size_unknown * buf __counted_by(count); + const struct size_unknown * buf __counted_by(count); // ok int count; }; diff --git a/clang/test/Sema/attr-counted-by-or-null-last-field.c b/clang/test/Sema/attr-counted-by-or-null-last-field.c index 12c0b6de44f72..60a1f571b19e9 100644 --- a/clang/test/Sema/attr-counted-by-or-null-last-field.c +++ b/clang/test/Sema/attr-counted-by-or-null-last-field.c @@ -118,12 +118,12 @@ struct annotated_with_anon_struct { struct count_unknown; struct on_member_ptr_incomplete_ty_ty_pos { int count; - struct count_unknown * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct count_unknown' is an incomplete type}} + struct count_unknown * ptr __counted_by_or_null(count); // ok }; struct on_member_ptr_incomplete_const_ty_ty_pos { int count; - const struct count_unknown * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'const struct count_unknown' is an incomplete type}} + const struct count_unknown * ptr __counted_by_or_null(count); // ok }; struct on_member_ptr_void_ty_ty_pos { diff --git a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c index 95f517e3144f7..2150c81f9e9be 100644 --- a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c +++ b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c @@ -20,13 +20,12 @@ struct on_member_pointer_complete_ty { }; struct on_member_pointer_incomplete_ty { - struct size_unknown * buf __counted_by_or_null(count); // expected-error{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}} + struct size_unknown * buf __counted_by_or_null(count); // ok int count; }; struct on_member_pointer_const_incomplete_ty { - // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}} - const struct size_unknown * buf __counted_by_or_null(count); + const struct size_unknown * buf __counted_by_or_null(count); // ok int count; }; diff --git a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c index 708bb727ce09d..0bb09059c97f9 100644 --- a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c +++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c @@ -22,14 +22,12 @@ struct on_member_pointer_complete_ty { struct on_member_pointer_incomplete_ty { int count; - // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}} - struct size_unknown * buf __counted_by_or_null(count); + struct size_unknown * buf __counted_by_or_null(count); // ok }; struct on_member_pointer_const_incomplete_ty { int count; - // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}} - const struct size_unknown * buf __counted_by_or_null(count); + const struct size_unknown * buf __counted_by_or_null(count); // ok }; struct on_member_pointer_void_ty { @@ -116,14 +114,12 @@ struct on_member_pointer_complete_ty_ty_pos { struct on_member_pointer_incomplete_ty_ty_pos { int count; - // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}} - struct size_unknown * __counted_by_or_null(count) buf; + struct size_unknown * __counted_by_or_null(count) buf; // ok }; struct on_member_pointer_const_incomplete_ty_ty_pos { int count; - // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}} - const struct size_unknown * __counted_by_or_null(count) buf; + const struct size_unknown * __counted_by_or_null(count) buf; // ok }; struct on_member_pointer_void_ty_ty_pos { diff --git a/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c b/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c new file mode 100644 index 0000000000000..746e62e0bb207 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c @@ -0,0 +1,622 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +// ============================================================================= +// # Struct incomplete type with attribute in the decl position +// ============================================================================= + +// Note: This could be considered misleading. The typedef isn't actually on this +// line. Also note the discrepancy in diagnostic count (27 vs 51) is due to +// the pointer arithmetic on incomplete pointee type diagnostic always using +// diagnostic text that refers to the underlying forward decl, even when the +// typedef is used. +// expected-note@+1 27{{forward declaration of 'Incomplete_t' (aka 'struct IncompleteTy')}} +struct IncompleteTy; // expected-note 51{{forward declaration of 'struct IncompleteTy'}} + +typedef struct IncompleteTy Incomplete_t; + +struct CBBufDeclPos { + int count; + struct IncompleteTy* buf __counted_by(count); // OK expected-note 27{{__counted_by attribute is here}} + Incomplete_t* buf_typedef __counted_by(count); // OK expected-note 27{{__counted_by attribute is here}} +}; + +void consume_struct_IncompleteTy(struct IncompleteTy* buf); + +int idx(void); + + + +void test_CBBufDeclPos(struct CBBufDeclPos* ptr) { + // =========================================================================== + // ## Local variable initialization + // =========================================================================== + struct CBBufDeclPos explicit_desig_init = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + .buf_typedef = 0x0 + }; + // Variable is not currently marked as invalid so uses of the variable allows + // diagnostics to fire. + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot use 'explicit_desig_init.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + void *tmp = explicit_desig_init.buf; + // expected-error@+1{{cannot use 'explicit_desig_init.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + void *tmp2 = explicit_desig_init.buf_typedef; + + struct CBBufDeclPos partial_explicit_desig_init = { + .count = 0, + // .buf and .buf_typedef are implicit zero initialized + // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + }; + + struct CBBufDeclPos implicit_full_init = { + 0 + // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + }; + // Variable is not currently marked as invalid so uses of the variable allows + // diagnostics to fire. + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + implicit_full_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + implicit_full_init.buf_typedef = 0x0; + // expected-error@+1{{cannot use 'implicit_full_init.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + void* tmp3 = implicit_full_init.buf; + // expected-error@+1{{cannot use 'implicit_full_init.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + void* tmp4 = implicit_full_init.buf_typedef; + + struct CBBufDeclPos explicit_non_desig_init = { + 0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + 0x0 + }; + + // =========================================================================== + // ## Assignment to fields + // =========================================================================== + struct CBBufDeclPos uninit; + uninit.count = 0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + uninit.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + uninit.buf_typedef = 0x0; + ptr->count = 0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + ptr->buf_typedef = 0x0; + + + // =========================================================================== + // ## Make sure modifying the fields through other assignment operators is not + // allowed + // =========================================================================== + uninit.buf++; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + ++uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + uninit.buf += 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + uninit.buf_typedef++; // // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + ++uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + + uninit.buf--; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + --uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + uninit.buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + uninit.buf_typedef--; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + --uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + + ptr->buf++; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + ++ptr->buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + ptr->buf += 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + ptr->buf--; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + --ptr->buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + ptr->buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}} + + ptr->buf_typedef++; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + ++ptr->buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + ptr->buf_typedef += 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + ptr->buf_typedef--; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + --ptr->buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + ptr->buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}} + + // =========================================================================== + // ## Use of fields in expressions + // =========================================================================== + // expected-error@+2{{cannot use 'uninit.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + void* addr = + ((char*) uninit.buf ) + 1; + // expected-error@+2{{cannot use 'uninit.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + void* addr_typedef = + ((char*) uninit.buf_typedef ) + 1; + // expected-error@+2{{cannot use 'ptr->buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + void* addr_ptr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + void* addr_ptr_typedef = + ((char*) ptr->buf_typedef ) + 1; + + // =========================================================================== + // ## Take address of fields + // =========================================================================== + // TODO: This should be forbidden, not because of the incomplete pointee type + // but because in the -fbounds-safety language model the address of a + // `counted_by` pointer cannot be taken to avoid it being possible to modify + // the `counted_by` pointer through another pointer. Whether or not this + // should be forbidden when `-fbounds-safety` is off is TBD. + // + // The incomplete pointee type isn't actually a problem here for + // `-fbounds-safety` because taking the address of a pointer returns a pointer + // that have the bounds of a single `void*`, so bounds checks on the resulting + // pointer don't need to know `sizeof(struct IncompleteTy)` but instead + // `sizeof(struct IncompleteTy* buf __counted_by(count))` which is just the + // size of a pointer. + void* take_addr = &uninit.buf; + void* take_addr_typedef = &uninit.buf_typedef; + void* take_addr_ptr = &ptr->buf; + void* take_addr_ptr_typedef = &ptr->buf_typedef; + + // expected-error@+1{{cannot use 'uninit.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + struct IncompleteTy* addr_elt_zero = &uninit.buf[0]; + // expected-error@+1{{cannot use 'uninit.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + struct IncompleteTy* addr_elt_idx = &uninit.buf[idx()]; + + // expected-error@+1{{cannot use 'uninit.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + struct IncompleteTy* addr_elt_zero_typedef = &uninit.buf_typedef[0]; + // expected-error@+1{{cannot use 'uninit.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + struct IncompleteTy* addr_elt_idx_typedef = &uninit.buf_typedef[idx()]; + + // expected-error@+1{{cannot use 'ptr->buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + struct IncompleteTy* addr_elt_zero_ptr = &ptr->buf[0]; + // expected-error@+1{{cannot use 'ptr->buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + struct IncompleteTy* addr_elt_idx_ptr = &ptr->buf[idx()]; + // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + struct IncompleteTy* addr_elt_zero_ptr_typedef = &ptr->buf_typedef[0]; + // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + struct IncompleteTy* addr_elt_idx_ptr_typedef = &ptr->buf_typedef[idx()]; + + // =========================================================================== + // ## Use fields as call arguments + // =========================================================================== + // expected-error@+1{{cannot use 'uninit.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + consume_struct_IncompleteTy(uninit.buf); + // expected-error@+1{{cannot use 'uninit.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + consume_struct_IncompleteTy(uninit.buf_typedef); + // expected-error@+1{{cannot use 'ptr->buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + consume_struct_IncompleteTy(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + consume_struct_IncompleteTy(ptr->buf_typedef); + + // =========================================================================== + // ## Use [] operator on fields + // =========================================================================== + // expected-error@+1 2{{cannot use 'uninit.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + uninit.buf[0] = uninit.buf[1]; + // expected-error@+1 2{{cannot use 'uninit.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + uninit.buf_typedef[0] = uninit.buf_typedef[1]; + // expected-error@+1 2{{cannot use 'ptr->buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + ptr->buf_typedef[0] = ptr->buf_typedef[1]; +} + +// ============================================================================= +// ## Global initialization +// ============================================================================= + +struct CBBufDeclPos global_explicit_desig_init = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + .buf_typedef = 0x0 +}; + +void use_global_explicit_desig_init(void) { + // Variable isn't marked as invalid so diagnostics still fire + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + global_explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + global_explicit_desig_init.buf_typedef = 0x0; +} + +struct CBBufDeclPos global_partial_explicit_desig_init = { + .count = 0, + // .buf and .buf_typedef are implicit zero initialized + // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} +}; + +struct CBBufDeclPos global_implicit_full_init = { + 0 + // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} +}; + +struct CBBufDeclPos global_explicit_non_desig_init = { + 0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}} + 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}} + 0x0 +}; + +extern struct CBBufDeclPos global_declaration; // OK + +// TODO: These tentative definitions are implicitly empty initialized to zero. +// This should generate an error diagnostic but currently doesn't. There should +// be a carve out to allow `__counted_by(0)` which is the only constant count +// version of the attribute where it is valid to assign NULL. +struct CBBufDeclPos global_tentative_defn; +static struct CBBufDeclPos global_tentative_defn2; + +// ============================================================================= +// ## Completing the definition of the type allows use of CBBufDeclPos fields +// ============================================================================= +struct IncompleteTy { + int field; +}; + +void test_CBBufDeclPos_completed(struct CBBufDeclPos* ptr) { + // Initialization is ok + struct CBBufDeclPos explicit_desig_init = { + .count = 0, + .buf = 0x0, + .buf_typedef = 0x0 + }; + + struct CBBufDeclPos partial_explicit_desig_init = { + .count = 0, + // .buf and .buf_typedef are implicit zero initialized + }; + + struct CBBufDeclPos implicit_full_init = {0}; + + struct CBBufDeclPos explicit_non_desig_init = { + 0, + 0x0, + 0x0 + }; + + // Assignment to fields is ok + ptr->buf = 0x0; + ptr->buf_typedef = 0x0; + + // Use of fields in expressions is ok + void* tmp = ptr->buf; + void* tmp2 = ptr->buf_typedef; + + // Take address of fields is ok + void* take_addr_ptr = &ptr->buf; + void* take_addr_ptr_typedef = &ptr->buf_typedef; + + struct IncompleteTy* addr_elt_zero_ptr = &ptr->buf[0]; + struct IncompleteTy* addr_elt_idx_ptr = &ptr->buf[idx()]; + struct IncompleteTy* addr_elt_zero_ptr_typedef = &ptr->buf_typedef[0]; + struct IncompleteTy* addr_elt_idx_ptr_typedef = &ptr->buf_typedef[idx()]; + + // As call arguments is ok + consume_struct_IncompleteTy(ptr->buf); + consume_struct_IncompleteTy(ptr->buf_typedef); + + // In [] operator is ok + ptr->buf[0] = ptr->buf[1]; + ptr->buf_typedef[0] = ptr->buf_typedef[1]; +} + +// Global initialization is ok + +struct CBBufDeclPos global_explicit_desig_init_completed = { + .count = 0, + .buf = 0x0, + .buf_typedef = 0x0 +}; + +struct CBBufDeclPos global_partial_explicit_desig_init_completed = { + .count = 0, + // .buf and .buf_typedef are implicit zero initialized +}; + +struct CBBufDeclPos global_implicit_full_init_completed = {0}; + +struct CBBufDeclPos global_explicit_non_desig_init_completed = { + 0, + 0x0, + 0x0 +}; + +extern struct CBBufDeclPos global_declaration; +struct CBBufDeclPos global_tentative_defn; +static struct CBBufDeclPos global_tentative_defn2; + +// ============================================================================= +// # Struct incomplete type with attribute in the pointer position +// ============================================================================= + +// expected-note@+1 8{{forward declaration of 'Incomplete_ty2' (aka 'struct IncompleteTy2')}} +struct IncompleteTy2; // expected-note 8{{forward declaration of 'struct IncompleteTy2'}} +typedef struct IncompleteTy2 Incomplete_ty2; + +void consume_struct_IncompleteTy2(struct IncompleteTy2* buf); + +struct CBBufTyPos { + int count; + struct IncompleteTy2* __counted_by(count) buf ; // OK expected-note 8{{__counted_by attribute is here}} + Incomplete_ty2 *__counted_by(count) buf_typedef; // OK expected-note 8{{__counted_by attribute is here}} +}; + +void use_CBBufTyPos(struct CBBufTyPos* ptr) { + struct CBBufTyPos explicit_desig_init = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufTyPos::buf' that has type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufTyPos::buf_typedef' that has type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}} + .buf_typedef = 0x0 + }; + + // Assignment + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf' that has type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf_typedef' that has type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf' that has type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf_typedef' that has type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}} + ptr->buf_typedef = 0x0; + + // Use + // expected-error@+2{{cannot use 'ptr->buf' with type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}} + void* addr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}} + void* addr_typedef = + ((char*) ptr->buf_typedef ) + 1; + + // expected-error@+1{{cannot use 'ptr->buf' with type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}} + consume_struct_IncompleteTy2(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}} + consume_struct_IncompleteTy2(ptr->buf_typedef); + + // expected-error@+1 2{{cannot use 'ptr->buf' with type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}} + ptr->buf_typedef[0] = ptr->buf_typedef[1]; +} + +struct CBBufTyPos global_explicit_desig_init_struct_type_pos = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufTyPos::buf' that has type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufTyPos::buf_typedef' that has type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}} + .buf_typedef = 0x0 +}; + +// Defining the type makes `CBBufTyPos` fields usable +struct IncompleteTy2 { + int field; +}; + +void use_CBBufTyPos_completed(struct CBBufTyPos* ptr) { + ptr->buf = 0x0; + ptr->buf_typedef = 0x0; + void* addr = ((char*) ptr->buf) + 1; + void* addr_typedef = ((char*) ptr->buf_typedef) + 1; +} + +// ============================================================================= +// # union incomplete type +// ============================================================================= + +// expected-note@+1 8{{forward declaration of 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy')}} +union IncompleteUnionTy; // expected-note 8{{forward declaration of 'union IncompleteUnionTy'}} +typedef union IncompleteUnionTy IncompleteUnion_ty; + +void consume_struct_IncompleteUnionTy(union IncompleteUnionTy* buf); + +struct CBBufUnionTyPos { + int count; + union IncompleteUnionTy* __counted_by(count) buf ; // OK expected-note 8{{__counted_by attribute is here}} + IncompleteUnion_ty *__counted_by(count) buf_typedef; // OK expected-note 8{{__counted_by attribute is here}} +}; + +void use_CBBufUnionTyPos(struct CBBufUnionTyPos* ptr) { + struct CBBufUnionTyPos explicit_desig_init = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf' that has type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' that has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}} + .buf_typedef = 0x0 + }; + + // Assignment + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf' that has type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' that has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf' that has type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' that has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}} + ptr->buf_typedef = 0x0; + + // Use + // expected-error@+2{{cannot use 'ptr->buf' with type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}} + void* addr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}} + void* addr_typedef = + ((char*) ptr->buf_typedef ) + 1; + + // expected-error@+1{{cannot use 'ptr->buf' with type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}} + consume_struct_IncompleteUnionTy(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}} + consume_struct_IncompleteUnionTy(ptr->buf_typedef); + + // expected-error@+1 2{{cannot use 'ptr->buf' with type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}} + ptr->buf_typedef[0] = ptr->buf_typedef[1]; +} + +struct CBBufUnionTyPos global_explicit_desig_init_union_type_pos = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf' that has type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' that has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}} + .buf_typedef = 0x0 +}; + +// Defining the type makes `CBBufUnionTyPos` fields usable +union IncompleteUnionTy { + int field; +}; + +void use_CBBufUnionTyPos_completed(struct CBBufUnionTyPos* ptr) { + ptr->buf = 0x0; + ptr->buf_typedef = 0x0; + void* addr = ((char*) ptr->buf) + 1; + void* addr_typedef = ((char*) ptr->buf_typedef) + 1; +} + +// ============================================================================= +// # enum incomplete type +// ============================================================================= + +// expected-note@+1 8{{forward declaration of 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy')}} +enum IncompleteEnumTy; // expected-note 8{{forward declaration of 'enum IncompleteEnumTy'}} +typedef enum IncompleteEnumTy IncompleteEnum_ty; + +void consume_struct_IncompleteEnumTy(enum IncompleteEnumTy* buf); + +struct CBBufEnumTyPos { + int count; + enum IncompleteEnumTy* __counted_by(count) buf ; // OK expected-note 8{{__counted_by attribute is here}} + IncompleteEnum_ty *__counted_by(count) buf_typedef; // OK expected-note 8{{__counted_by attribute is here}} +}; + +void use_CBBufEnumTyPos(struct CBBufEnumTyPos* ptr) { + struct CBBufEnumTyPos explicit_desig_init = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf' that has type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' that has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}} + .buf_typedef = 0x0 + }; + + // Assignment + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf' that has type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' that has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf' that has type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' that has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}} + ptr->buf_typedef = 0x0; + + // Use + // expected-error@+2{{cannot use 'ptr->buf' with type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}} + void* addr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}} + void* addr_typedef = + ((char*) ptr->buf_typedef ) + 1; + + // expected-error@+1{{cannot use 'ptr->buf' with type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}} + consume_struct_IncompleteEnumTy(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}} + consume_struct_IncompleteEnumTy(ptr->buf_typedef); + + // expected-error@+1 2{{cannot use 'ptr->buf' with type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}} + ptr->buf_typedef[0] = ptr->buf_typedef[1]; +} + +struct CBBufEnumTyPos global_explicit_desig_init_enum_type_pos = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf' that has type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' that has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}} + .buf_typedef = 0x0 +}; + +// Defining the type makes `CBBufEnumTyPos` fields usable +enum IncompleteEnumTy { + ONE, + TWO +}; + +void use_CBBufEnumTyPos_completed(struct CBBufEnumTyPos* ptr) { + ptr->buf = 0x0; + ptr->buf_typedef = 0x0; + void* addr = ((char*) ptr->buf) + 1; + void* addr_typedef = ((char*) ptr->buf_typedef) + 1; +} + +// Make a complete enum by providing an underlying type +enum CompleteEnumTy : unsigned; +typedef enum CompleteEnumTy CompleteEnum_ty; +struct CBBufEnumTyPos2 { + int count; + enum CompleteEnumTy* __counted_by(count) buf; + CompleteEnum_ty *__counted_by(count) buf_typedef; +}; + +void use_CBBufEnumTyPos2(struct CBBufEnumTyPos2* ptr) { + struct CBBufEnumTyPos2 explicit_desig_init = { + .count = 0, + .buf = 0x0, // OK + .buf_typedef = 0x0 // OK + }; +} + +// Make a complete enum by providing a concrete declaration +enum CompleteEnumTy2 { + VALUE_ONE, + VALUE_TWO +}; +typedef enum CompleteEnumTy2 CompleteEnum_ty2; +struct CBBufEnumTyPos3 { + int count; + enum CompleteEnumTy2* __counted_by(count) buf; + CompleteEnum_ty2 *__counted_by(count) buf_typedef; +}; + +void use_CBBufEnumTyPos3(struct CBBufEnumTyPos3* ptr) { + struct CBBufEnumTyPos3 explicit_desig_init = { + .count = 0, + .buf = 0x0, // OK + .buf_typedef = 0x0 // OK + }; +} + + +// ============================================================================= +// # Array of __counted_by pointers +// ============================================================================= + +struct IncompleteTy3; + +struct CBBufFAMofCountedByPtrs { + int size; + // TODO: This is misleading. The attribute is written in the type position + // but clang currently doesn't treat it like that and it gets treated as + // an attribute on the array, rather than on the element type. + struct IncompleteTy3* __counted_by(size) arr[]; +}; + +void arr_of_counted_by_ptr(struct CBBufFAMofCountedByPtrs* ptr) { + // TODO: Should be disallowed once parsing attributes in the type position + // works. + ptr->arr[0] = 0x0; + void* addr = ((char*) ptr->arr[0]) + 1; +} diff --git a/clang/test/Sema/attr-counted-by-struct-ptrs.c b/clang/test/Sema/attr-counted-by-struct-ptrs.c index 321d6aafbeba2..c05d18262e2b7 100644 --- a/clang/test/Sema/attr-counted-by-struct-ptrs.c +++ b/clang/test/Sema/attr-counted-by-struct-ptrs.c @@ -21,14 +21,12 @@ struct on_member_pointer_complete_ty { struct on_member_pointer_incomplete_ty { int count; - // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}} - struct size_unknown * buf __counted_by(count); + struct size_unknown * buf __counted_by(count); // ok }; struct on_member_pointer_const_incomplete_ty { int count; - // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}} - const struct size_unknown * buf __counted_by(count); + const struct size_unknown * buf __counted_by(count); // ok }; struct on_member_pointer_void_ty { @@ -115,14 +113,12 @@ struct on_member_pointer_complete_ty_ty_pos { struct on_member_pointer_incomplete_ty_ty_pos { int count; - // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}} - struct size_unknown * __counted_by(count) buf; + struct size_unknown * __counted_by(count) buf; // ok }; struct on_member_pointer_const_incomplete_ty_ty_pos { int count; - // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}} - const struct size_unknown * __counted_by(count) buf; + const struct size_unknown * __counted_by(count) buf; // ok }; struct on_member_pointer_void_ty_ty_pos { diff --git a/clang/test/Sema/attr-counted-by-vla.c b/clang/test/Sema/attr-counted-by-vla.c index 35737c03f3222..5b1b4833dce7d 100644 --- a/clang/test/Sema/attr-counted-by-vla.c +++ b/clang/test/Sema/attr-counted-by-vla.c @@ -98,8 +98,7 @@ struct array_of_ints_count { struct not_a_fam { int count; - // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct bar' is an incomplete type}} - struct bar *non_fam __counted_by(count); + struct bar *non_fam __counted_by(count); // ok }; struct not_a_c99_fam { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits