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..988a7dda21c5e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6710,6 +6710,30 @@ 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 with|" // AA_Assigning, + "pass argument to %select{parameter|parameter '%1'}2 with|" // AA_Passing, + "return|" // AA_Returning, + "convert to|" // AA_Converting (UNUSED) + "%select{|implicitly }3initialize %select{object|'%1'}2 with|" // AA_Initializing, + "pass argument to parameter with|" // AA_Sending (UNUSED) + "cast to|" // AA_Casting (UNUSED) + "pass argument to parameter with" // AA_Passing_CFAudited (UNUSED) + "}0 '%5' attributed type %4 because the pointee type %6 is incomplete">; + +def err_counted_by_on_incomplete_type_on_use : Error < + "cannot %select{" + "use '%1' with '%4' attributed|" // Generic expr + "call '%1' with '%4' attributed return" // CallExpr + "}0 type %2 because the pointee type %3 is incomplete">; + +def note_counted_by_consider_completing_pointee_ty : Note< + "consider providing a complete definition for %0">; + +def note_counted_by_consider_using_sized_by : Note< + "consider using '__sized_by%select{|_or_null}0' instead of " + "'__counted_by%select{|_or_null}0'">; def warn_counted_by_attr_elt_type_unknown_size : Warning, 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..1b356eec6b78a 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,182 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, return false; } +static void EmitIncompleteCountedByPointeeNotes(Sema &S, + const CountAttributedType *CATy, + NamedDecl *IncompleteTyDecl) { + assert(IncompleteTyDecl == nullptr || isa(IncompleteTyDecl)); + + if (IncompleteTyDecl) { + // Suggest completing the pointee type if its a named typed (i.e. + // IncompleteTyDecl isn't nullptr). Suggest this first as it is more likely + // to be the correct fix. + // + // 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. + S.Diag(IncompleteTyDecl->getBeginLoc(), + diag::note_counted_by_consider_completing_pointee_ty) + << CATy->getPointeeType(); + } + + // Suggest using __sized_by(_or_null) instead of __counted_by(_or_null) as + // __sized_by(_or_null) doesn't have the complete type restriction. + // + // We use the source range of the expression on the CountAttributedType as an + // approximation for the source range of the attribute. This isn't quite right + // but isn't easy to fix right now. + // + // TODO: Implement logic to find the relevant TypeLoc for the attribute and + // get the SourceRange from that (#113582). + // + // TODO: We should emit a fix-it here. + SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange(); + S.Diag(AttrSrcRange.getBegin(), diag::note_counted_by_consider_using_sized_by) + << CATy->isOrNull() << AttrSrcRange; +} + +static std::tuple +GetCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND) { + auto *CATy = Ty->getAs(); + // 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(Action) << AssigneeStr << (AssigneeStr.size() > 0) + << isa(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(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(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(LHSExpr->IgnoreParenCasts())) { + Assignee = DR->getDecl(); + } else if (auto *ME = dyn_cast(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-completable-incomplete-pointee.c b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c new file mode 100644 index 0000000000000..cff5a14c70b99 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c @@ -0,0 +1,631 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s + +#define __counted_by_or_null(f) __attribute__((counted_by_or_null(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@+3 27{{consider providing a complete definition for 'Incomplete_t' (aka 'struct IncompleteTy')}} +// The 'forward declaration' notes come from 'arithmetic on a pointer to an incomplete type' errors +// expected-note@+1 24{{forward declaration of 'struct IncompleteTy'}} +struct IncompleteTy; // expected-note 27{{consider providing a complete definition for 'struct IncompleteTy'}} + +typedef struct IncompleteTy Incomplete_t; + +struct CBBufDeclPos { + int count; + struct IncompleteTy* buf __counted_by_or_null(count); // OK expected-note 27{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + Incomplete_t* buf_typedef __counted_by_or_null(count); // OK expected-note 27{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +}; + +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' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + .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' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot use 'explicit_desig_init.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + void *tmp = explicit_desig_init.buf; + // expected-error@+1{{cannot use 'explicit_desig_init.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + }; + + struct CBBufDeclPos implicit_full_init = { + 0 + // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + }; + // Variable is not currently marked as invalid so uses of the variable allows + // diagnostics to fire. + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + implicit_full_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + implicit_full_init.buf_typedef = 0x0; + // expected-error@+1{{cannot use 'implicit_full_init.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + void* tmp3 = implicit_full_init.buf; + // expected-error@+1{{cannot use 'implicit_full_init.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + void* tmp4 = implicit_full_init.buf_typedef; + + struct CBBufDeclPos explicit_non_desig_init = { + 0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 0x0 + }; + + + + + // =========================================================================== + // ## Assignment to fields + // =========================================================================== + struct CBBufDeclPos uninit; + uninit.count = 0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + uninit.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + uninit.buf_typedef = 0x0; + ptr->count = 0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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 '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + void* addr = + ((char*) uninit.buf ) + 1; + // expected-error@+2{{cannot use 'uninit.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + void* addr_typedef = + ((char*) uninit.buf_typedef ) + 1; + // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + void* addr_ptr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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_or_null(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 '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + struct IncompleteTy* addr_elt_zero = &uninit.buf[0]; + // expected-error@+1{{cannot use 'uninit.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + struct IncompleteTy* addr_elt_idx = &uninit.buf[idx()]; + + // expected-error@+1{{cannot use 'uninit.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + struct IncompleteTy* addr_elt_zero_typedef = &uninit.buf_typedef[0]; + // expected-error@+1{{cannot use 'uninit.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + struct IncompleteTy* addr_elt_idx_typedef = &uninit.buf_typedef[idx()]; + + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + struct IncompleteTy* addr_elt_zero_ptr = &ptr->buf[0]; + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + struct IncompleteTy* addr_elt_idx_ptr = &ptr->buf[idx()]; + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + struct IncompleteTy* addr_elt_zero_ptr_typedef = &ptr->buf_typedef[0]; + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + struct IncompleteTy* addr_elt_idx_ptr_typedef = &ptr->buf_typedef[idx()]; + + + // =========================================================================== + // ## Use fields as call arguments + // =========================================================================== + // expected-error@+1{{cannot use 'uninit.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + consume_struct_IncompleteTy(uninit.buf); + // expected-error@+1{{cannot use 'uninit.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + consume_struct_IncompleteTy(uninit.buf_typedef); + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + consume_struct_IncompleteTy(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + consume_struct_IncompleteTy(ptr->buf_typedef); + + // =========================================================================== + // ## Use [] operator on fields + // =========================================================================== + // expected-error@+1 2{{cannot use 'uninit.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + uninit.buf[0] = uninit.buf[1]; + // expected-error@+1 2{{cannot use 'uninit.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + uninit.buf_typedef[0] = uninit.buf_typedef[1]; + // expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + .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' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + global_explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} +}; + +struct CBBufDeclPos global_implicit_full_init = { + 0 + // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} +}; + +struct CBBufDeclPos global_explicit_non_desig_init = { + 0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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_or_null(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{{consider providing a complete definition for 'Incomplete_ty2' (aka 'struct IncompleteTy2')}} +struct IncompleteTy2; // expected-note 8{{consider providing a complete definition for 'struct IncompleteTy2'}} +typedef struct IncompleteTy2 Incomplete_ty2; + +void consume_struct_IncompleteTy2(struct IncompleteTy2* buf); + +struct CBBufTyPos { + int count; + struct IncompleteTy2* __counted_by_or_null(count) buf ; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + Incomplete_ty2 *__counted_by_or_null(count) buf_typedef; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +}; + +void use_CBBufTyPos(struct CBBufTyPos* ptr) { + struct CBBufTyPos explicit_desig_init = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufTyPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + .buf_typedef = 0x0 + }; + + // Assignment + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + ptr->buf_typedef = 0x0; + + // Use + // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + void* addr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + void* addr_typedef = + ((char*) ptr->buf_typedef ) + 1; + + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + consume_struct_IncompleteTy2(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + consume_struct_IncompleteTy2(ptr->buf_typedef); + + // expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + 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' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + .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{{consider providing a complete definition for 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy')}} +union IncompleteUnionTy; // expected-note 8{{consider providing a complete definition for 'union IncompleteUnionTy'}} +typedef union IncompleteUnionTy IncompleteUnion_ty; + +void consume_struct_IncompleteUnionTy(union IncompleteUnionTy* buf); + +struct CBBufUnionTyPos { + int count; + union IncompleteUnionTy* __counted_by_or_null(count) buf ; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + IncompleteUnion_ty *__counted_by_or_null(count) buf_typedef; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +}; + +void use_CBBufUnionTyPos(struct CBBufUnionTyPos* ptr) { + struct CBBufUnionTyPos explicit_desig_init = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + .buf_typedef = 0x0 + }; + + // Assignment + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + ptr->buf_typedef = 0x0; + + // Use + // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + void* addr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + void* addr_typedef = + ((char*) ptr->buf_typedef ) + 1; + + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + consume_struct_IncompleteUnionTy(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + consume_struct_IncompleteUnionTy(ptr->buf_typedef); + + // expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + 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' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + .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{{consider providing a complete definition for 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy')}} +enum IncompleteEnumTy; // expected-note 8{{consider providing a complete definition for 'enum IncompleteEnumTy'}} +typedef enum IncompleteEnumTy IncompleteEnum_ty; + +void consume_struct_IncompleteEnumTy(enum IncompleteEnumTy* buf); + +struct CBBufEnumTyPos { + int count; + enum IncompleteEnumTy* __counted_by_or_null(count) buf ; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + IncompleteEnum_ty *__counted_by_or_null(count) buf_typedef; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +}; + +void use_CBBufEnumTyPos(struct CBBufEnumTyPos* ptr) { + struct CBBufEnumTyPos explicit_desig_init = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + .buf_typedef = 0x0 + }; + + // Assignment + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + ptr->buf_typedef = 0x0; + + // Use + // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + void* addr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + void* addr_typedef = + ((char*) ptr->buf_typedef ) + 1; + + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + consume_struct_IncompleteEnumTy(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + consume_struct_IncompleteEnumTy(ptr->buf_typedef); + + // expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + 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' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + .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_or_null(count) buf; + CompleteEnum_ty *__counted_by_or_null(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_or_null(count) buf; + CompleteEnum_ty2 *__counted_by_or_null(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_or_null 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. + // expected-error@+1{{'counted_by_or_null' only applies to pointers; did you mean to use 'counted_by'?}} + struct IncompleteTy3* __counted_by_or_null(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-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..d28a2086b51b8 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c @@ -0,0 +1,630 @@ +// 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@+3 27{{consider providing a complete definition for 'Incomplete_t' (aka 'struct IncompleteTy')}} +// The 'forward declaration' notes come from 'arithmetic on a pointer to an incomplete type' errors +// expected-note@+1 24{{forward declaration of 'struct IncompleteTy'}} +struct IncompleteTy; // expected-note 27{{consider providing a complete definition for 'struct IncompleteTy'}} + +typedef struct IncompleteTy Incomplete_t; + +struct CBBufDeclPos { + int count; + struct IncompleteTy* buf __counted_by(count); // OK expected-note 27{{consider using '__sized_by' instead of '__counted_by'}} + Incomplete_t* buf_typedef __counted_by(count); // OK expected-note 27{{consider using '__sized_by' instead of '__counted_by'}} +}; + +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' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + .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' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot use 'explicit_desig_init.buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + void *tmp = explicit_desig_init.buf; + // expected-error@+1{{cannot use 'explicit_desig_init.buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + }; + + struct CBBufDeclPos implicit_full_init = { + 0 + // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + }; + // Variable is not currently marked as invalid so uses of the variable allows + // diagnostics to fire. + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + implicit_full_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + implicit_full_init.buf_typedef = 0x0; + // expected-error@+1{{cannot use 'implicit_full_init.buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + void* tmp3 = implicit_full_init.buf; + // expected-error@+1{{cannot use 'implicit_full_init.buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + void* tmp4 = implicit_full_init.buf_typedef; + + struct CBBufDeclPos explicit_non_desig_init = { + 0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 0x0 + }; + + + + + // =========================================================================== + // ## Assignment to fields + // =========================================================================== + struct CBBufDeclPos uninit; + uninit.count = 0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + uninit.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + uninit.buf_typedef = 0x0; + ptr->count = 0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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 '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + void* addr = + ((char*) uninit.buf ) + 1; + // expected-error@+2{{cannot use 'uninit.buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + void* addr_typedef = + ((char*) uninit.buf_typedef ) + 1; + // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + void* addr_ptr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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 '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + struct IncompleteTy* addr_elt_zero = &uninit.buf[0]; + // expected-error@+1{{cannot use 'uninit.buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + struct IncompleteTy* addr_elt_idx = &uninit.buf[idx()]; + + // expected-error@+1{{cannot use 'uninit.buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + struct IncompleteTy* addr_elt_zero_typedef = &uninit.buf_typedef[0]; + // expected-error@+1{{cannot use 'uninit.buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + struct IncompleteTy* addr_elt_idx_typedef = &uninit.buf_typedef[idx()]; + + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + struct IncompleteTy* addr_elt_zero_ptr = &ptr->buf[0]; + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + struct IncompleteTy* addr_elt_idx_ptr = &ptr->buf[idx()]; + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + struct IncompleteTy* addr_elt_zero_ptr_typedef = &ptr->buf_typedef[0]; + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + struct IncompleteTy* addr_elt_idx_ptr_typedef = &ptr->buf_typedef[idx()]; + + + // =========================================================================== + // ## Use fields as call arguments + // =========================================================================== + // expected-error@+1{{cannot use 'uninit.buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + consume_struct_IncompleteTy(uninit.buf); + // expected-error@+1{{cannot use 'uninit.buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + consume_struct_IncompleteTy(uninit.buf_typedef); + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + consume_struct_IncompleteTy(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + consume_struct_IncompleteTy(ptr->buf_typedef); + + // =========================================================================== + // ## Use [] operator on fields + // =========================================================================== + // expected-error@+1 2{{cannot use 'uninit.buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + uninit.buf[0] = uninit.buf[1]; + // expected-error@+1 2{{cannot use 'uninit.buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + uninit.buf_typedef[0] = uninit.buf_typedef[1]; + // expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + .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' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + global_explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} +}; + +struct CBBufDeclPos global_implicit_full_init = { + 0 + // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + // expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} +}; + +struct CBBufDeclPos global_explicit_non_desig_init = { + 0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}} + 0x0, + // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}} + 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{{consider providing a complete definition for 'Incomplete_ty2' (aka 'struct IncompleteTy2')}} +struct IncompleteTy2; // expected-note 8{{consider providing a complete definition for '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{{consider using '__sized_by' instead of '__counted_by'}} + Incomplete_ty2 *__counted_by(count) buf_typedef; // OK expected-note 8{{consider using '__sized_by' instead of '__counted_by'}} +}; + +void use_CBBufTyPos(struct CBBufTyPos* ptr) { + struct CBBufTyPos explicit_desig_init = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufTyPos::buf' with '__counted_by' attributed type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufTyPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + .buf_typedef = 0x0 + }; + + // Assignment + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf' with '__counted_by' attributed type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf' with '__counted_by' attributed type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufTyPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + ptr->buf_typedef = 0x0; + + // Use + // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + void* addr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + void* addr_typedef = + ((char*) ptr->buf_typedef ) + 1; + + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by' attributed type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + consume_struct_IncompleteTy2(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + consume_struct_IncompleteTy2(ptr->buf_typedef); + + // expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + 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' with '__counted_by' attributed type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufTyPos::buf_typedef' with '__counted_by' attributed type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}} + .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{{consider providing a complete definition for 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy')}} +union IncompleteUnionTy; // expected-note 8{{consider providing a complete definition for '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{{consider using '__sized_by' instead of '__counted_by'}} + IncompleteUnion_ty *__counted_by(count) buf_typedef; // OK expected-note 8{{consider using '__sized_by' instead of '__counted_by'}} +}; + +void use_CBBufUnionTyPos(struct CBBufUnionTyPos* ptr) { + struct CBBufUnionTyPos explicit_desig_init = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf' with '__counted_by' attributed type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' with '__counted_by' attributed type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + .buf_typedef = 0x0 + }; + + // Assignment + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf' with '__counted_by' attributed type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' with '__counted_by' attributed type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf' with '__counted_by' attributed type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' with '__counted_by' attributed type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + ptr->buf_typedef = 0x0; + + // Use + // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + void* addr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + void* addr_typedef = + ((char*) ptr->buf_typedef ) + 1; + + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by' attributed type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + consume_struct_IncompleteUnionTy(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + consume_struct_IncompleteUnionTy(ptr->buf_typedef); + + // expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + 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' with '__counted_by' attributed type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' with '__counted_by' attributed type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}} + .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{{consider providing a complete definition for 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy')}} +enum IncompleteEnumTy; // expected-note 8{{consider providing a complete definition for '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{{consider using '__sized_by' instead of '__counted_by'}} + IncompleteEnum_ty *__counted_by(count) buf_typedef; // OK expected-note 8{{consider using '__sized_by' instead of '__counted_by'}} +}; + +void use_CBBufEnumTyPos(struct CBBufEnumTyPos* ptr) { + struct CBBufEnumTyPos explicit_desig_init = { + .count = 0, + // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf' with '__counted_by' attributed type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' with '__counted_by' attributed type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + .buf_typedef = 0x0 + }; + + // Assignment + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf' with '__counted_by' attributed type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + explicit_desig_init.buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' with '__counted_by' attributed type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + explicit_desig_init.buf_typedef = 0x0; + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf' with '__counted_by' attributed type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + ptr->buf = 0x0; + // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' with '__counted_by' attributed type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + ptr->buf_typedef = 0x0; + + // Use + // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + void* addr = + ((char*) ptr->buf ) + 1; + // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + void* addr_typedef = + ((char*) ptr->buf_typedef ) + 1; + + // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by' attributed type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + consume_struct_IncompleteEnumTy(ptr->buf); + // expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + consume_struct_IncompleteEnumTy(ptr->buf_typedef); + + // expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + ptr->buf[0] = ptr->buf[1]; + // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + 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' with '__counted_by' attributed type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}} + .buf = 0x0, + // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' with '__counted_by' attributed type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}} + .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 {