Author: higher-performance Date: 2025-01-14T13:31:12-05:00 New Revision: 1594413d5edf6a47d4100cb6a2bc613cfbb92beb
URL: https://github.com/llvm/llvm-project/commit/1594413d5edf6a47d4100cb6a2bc613cfbb92beb DIFF: https://github.com/llvm/llvm-project/commit/1594413d5edf6a47d4100cb6a2bc613cfbb92beb.diff LOG: Add Clang attribute to ensure that fields are initialized explicitly (#102040) This is a new Clang-specific attribute to ensure that field initializations are performed explicitly. For example, if we have ``` struct B { [[clang::explicit]] int f1; }; ``` then the diagnostic would trigger if we do `B b{};`: ``` field 'f1' is left uninitialized, but was marked as requiring initialization ``` This prevents callers from accidentally forgetting to initialize fields, particularly when new fields are added to the class. Added: Modified: clang/include/clang/AST/Decl.h clang/include/clang/AST/DeclBase.h clang/include/clang/Basic/Attr.td clang/include/clang/Basic/AttrDocs.td clang/include/clang/Basic/DiagnosticASTKinds.td clang/include/clang/Basic/DiagnosticGroups.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/AST/Decl.cpp clang/lib/AST/DeclCXX.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaInit.cpp clang/lib/Serialization/ASTReaderDecl.cpp clang/lib/Serialization/ASTWriterDecl.cpp clang/test/Misc/pragma-attribute-supported-attributes-list.test clang/test/Sema/uninit-variables.c clang/test/SemaCXX/uninitialized.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 9c470f09406378..27a91a2d07210b 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -4285,6 +4285,14 @@ class RecordDecl : public TagDecl { RecordDeclBits.HasNonTrivialToPrimitiveCopyCUnion = V; } + bool hasUninitializedExplicitInitFields() const { + return RecordDeclBits.HasUninitializedExplicitInitFields; + } + + void setHasUninitializedExplicitInitFields(bool V) { + RecordDeclBits.HasUninitializedExplicitInitFields = V; + } + /// Determine whether this class can be passed in registers. In C++ mode, /// it must have at least one trivial, non-deleted copy or move constructor. /// FIXME: This should be set as part of completeDefinition. diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 77abd8b657a616..1c48c91bbe5a46 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1446,6 +1446,9 @@ class DeclContext { /// hasLazyLocalLexicalLookups, hasLazyExternalLexicalLookups friend class ASTWriter; +protected: + enum { NumOdrHashBits = 25 }; + // We use uint64_t in the bit-fields below since some bit-fields // cross the unsigned boundary and this breaks the packing. @@ -1667,6 +1670,14 @@ class DeclContext { LLVM_PREFERRED_TYPE(bool) uint64_t HasNonTrivialToPrimitiveCopyCUnion : 1; + /// True if any field is marked as requiring explicit initialization with + /// [[clang::requires_explicit_initialization]]. + /// In C++, this is also set for types without a user-provided default + /// constructor, and is propagated from any base classes and/or member + /// variables whose types are aggregates. + LLVM_PREFERRED_TYPE(bool) + uint64_t HasUninitializedExplicitInitFields : 1; + /// Indicates whether this struct is destroyed in the callee. LLVM_PREFERRED_TYPE(bool) uint64_t ParamDestroyedInCallee : 1; @@ -1681,7 +1692,7 @@ class DeclContext { /// True if a valid hash is stored in ODRHash. This should shave off some /// extra storage and prevent CXXRecordDecl to store unused bits. - uint64_t ODRHash : 26; + uint64_t ODRHash : NumOdrHashBits; }; /// Number of inherited and non-inherited bits in RecordDeclBitfields. diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a752d94b06fad6..b7d9357e1b4e18 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1902,6 +1902,13 @@ def Leaf : InheritableAttr { let SimpleHandler = 1; } +def ExplicitInit : InheritableAttr { + let Spellings = [Clang<"requires_explicit_initialization">]; + let Subjects = SubjectList<[Field], ErrorDiag>; + let Documentation = [ExplicitInitDocs]; + let SimpleHandler = 1; +} + def LifetimeBound : DeclOrTypeAttr { let Spellings = [Clang<"lifetimebound", 0>]; let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index e10f24e239eceb..b949a105e13317 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1684,6 +1684,55 @@ is not specified. }]; } +def ExplicitInitDocs : Documentation { + let Category = DocCatField; + let Content = [{ +The ``clang::requires_explicit_initialization`` attribute indicates that a +field of an aggregate must be initialized explicitly by the user when an object +of the aggregate type is constructed. The attribute supports both C and C++, +but its usage is invalid on non-aggregates. + +Note that this attribute is *not* a memory safety feature, and is *not* intended +to guard against use of uninitialized memory. + +Rather, it is intended for use in "parameter-objects", used to simulate, +for example, the passing of named parameters. +The attribute generates a warning when explicit initializers for such +variables are not provided (this occurs regardless of whether any in-class field +initializers exist): + +.. code-block:: c++ + + struct Buffer { + void *address [[clang::requires_explicit_initialization]]; + size_t length [[clang::requires_explicit_initialization]] = 0; + }; + + struct ArrayIOParams { + size_t count [[clang::requires_explicit_initialization]]; + size_t element_size [[clang::requires_explicit_initialization]]; + int flags = 0; + }; + + size_t ReadArray(FILE *file, struct Buffer buffer, + struct ArrayIOParams params); + + int main() { + unsigned int buf[512]; + ReadArray(stdin, { + buf + // warning: field 'length' is not explicitly initialized + }, { + .count = sizeof(buf) / sizeof(*buf), + // warning: field 'element_size' is not explicitly initialized + // (Note that a missing initializer for 'flags' is not diagnosed, because + // the field is not marked as requiring explicit initialization.) + }); + } + + }]; +} + def NoUniqueAddressDocs : Documentation { let Category = DocCatField; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index ab432606edbc4c..ac53778339a20d 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -396,6 +396,16 @@ def note_constexpr_assumption_failed : Note< def err_experimental_clang_interp_failed : Error< "the experimental clang interpreter failed to evaluate an expression">; +def warn_attribute_needs_aggregate : Warning< + "%0 attribute is ignored in non-aggregate type %1">, + InGroup<IgnoredAttributes>; + +def warn_cxx20_compat_requires_explicit_init_non_aggregate : Warning< + "explicit initialization of field %1 will not be enforced in C++20 and later " + "because %2 has a user-declared constructor, making the type no longer an " + "aggregate">, + DefaultIgnore, InGroup<CXX20Compat>; + def warn_integer_constant_overflow : Warning< "overflow in expression; result is %0 with type %1">, InGroup<DiagGroup<"integer-overflow">>; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 594e99a19b64d6..b0ad76026fdb35 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -796,6 +796,7 @@ def Trigraphs : DiagGroup<"trigraphs">; def UndefinedReinterpretCast : DiagGroup<"undefined-reinterpret-cast">; def ReinterpretBaseClass : DiagGroup<"reinterpret-base-class">; def Unicode : DiagGroup<"unicode">; +def UninitializedExplicitInit : DiagGroup<"uninitialized-explicit-init">; def UninitializedMaybe : DiagGroup<"conditional-uninitialized">; def UninitializedSometimes : DiagGroup<"sometimes-uninitialized">; def UninitializedStaticSelfInit : DiagGroup<"static-self-init">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8be4f946dce1cc..67c15e7c475943 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2347,6 +2347,9 @@ def err_init_reference_member_uninitialized : Error< "reference member of type %0 uninitialized">; def note_uninit_reference_member : Note< "uninitialized reference member is here">; +def warn_field_requires_explicit_init : Warning< + "field %select{%1|in %1}0 requires explicit initialization but is not " + "explicitly initialized">, InGroup<UninitializedExplicitInit>; def warn_field_is_uninit : Warning<"field %0 is uninitialized when used here">, InGroup<Uninitialized>; def warn_base_class_is_uninit : Warning< diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 31749e46458d6a..30341b046f95e3 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5031,6 +5031,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C, setHasNonTrivialToPrimitiveDefaultInitializeCUnion(false); setHasNonTrivialToPrimitiveDestructCUnion(false); setHasNonTrivialToPrimitiveCopyCUnion(false); + setHasUninitializedExplicitInitFields(false); setParamDestroyedInCallee(false); setArgPassingRestrictions(RecordArgPassingKind::CanPassInRegs); setIsRandomized(false); @@ -5231,9 +5232,10 @@ unsigned RecordDecl::getODRHash() { // Only calculate hash on first call of getODRHash per record. ODRHash Hash; Hash.AddRecordDecl(this); - // For RecordDecl the ODRHash is stored in the remaining 26 - // bit of RecordDeclBits, adjust the hash to accomodate. - setODRHash(Hash.CalculateHash() >> 6); + // For RecordDecl the ODRHash is stored in the remaining + // bits of RecordDeclBits, adjust the hash to accommodate. + static_assert(sizeof(Hash.CalculateHash()) * CHAR_BIT == 32); + setODRHash(Hash.CalculateHash() >> (32 - NumOdrHashBits)); return RecordDeclBits.ODRHash; } diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 4163342118c2dc..44f45898fb483d 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -29,6 +29,7 @@ #include "clang/AST/TypeLoc.h" #include "clang/AST/UnresolvedSet.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticAST.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" @@ -457,6 +458,10 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, if (BaseClassDecl->hasMutableFields()) data().HasMutableFields = true; + if (BaseClassDecl->hasUninitializedExplicitInitFields() && + BaseClassDecl->isAggregate()) + setHasUninitializedExplicitInitFields(true); + if (BaseClassDecl->hasUninitializedReferenceMember()) data().HasUninitializedReferenceMember = true; @@ -1113,6 +1118,9 @@ void CXXRecordDecl::addedMember(Decl *D) { } else if (!T.isCXX98PODType(Context)) data().PlainOldData = false; + if (Field->hasAttr<ExplicitInitAttr>()) + setHasUninitializedExplicitInitFields(true); + if (T->isReferenceType()) { if (!Field->hasInClassInitializer()) data().HasUninitializedReferenceMember = true; @@ -1372,6 +1380,10 @@ void CXXRecordDecl::addedMember(Decl *D) { if (!FieldRec->hasCopyAssignmentWithConstParam()) data().ImplicitCopyAssignmentHasConstParam = false; + if (FieldRec->hasUninitializedExplicitInitFields() && + FieldRec->isAggregate()) + setHasUninitializedExplicitInitFields(true); + if (FieldRec->hasUninitializedReferenceMember() && !Field->hasInClassInitializer()) data().HasUninitializedReferenceMember = true; @@ -2188,6 +2200,33 @@ void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) { for (conversion_iterator I = conversion_begin(), E = conversion_end(); I != E; ++I) I.setAccess((*I)->getAccess()); + + ASTContext &Context = getASTContext(); + + if (isAggregate() && hasUserDeclaredConstructor() && + !Context.getLangOpts().CPlusPlus20) { + // Diagnose any aggregate behavior changes in C++20 + for (const FieldDecl *FD : fields()) { + if (const auto *AT = FD->getAttr<ExplicitInitAttr>()) + Context.getDiagnostics().Report( + AT->getLocation(), + diag::warn_cxx20_compat_requires_explicit_init_non_aggregate) + << AT << FD << Context.getRecordType(this); + } + } + + if (!isAggregate() && hasUninitializedExplicitInitFields()) { + // Diagnose any fields that required explicit initialization in a + // non-aggregate type. (Note that the fields may not be directly in this + // type, but in a subobject. In such cases we don't emit diagnoses here.) + for (const FieldDecl *FD : fields()) { + if (const auto *AT = FD->getAttr<ExplicitInitAttr>()) + Context.getDiagnostics().Report(AT->getLocation(), + diag::warn_attribute_needs_aggregate) + << AT << Context.getRecordType(this); + } + setHasUninitializedExplicitInitFields(false); + } } bool CXXRecordDecl::mayBeAbstract() const { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index fd3a5ec49771d9..704cb82b291cc7 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -19232,6 +19232,8 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, if (FT.hasNonTrivialToPrimitiveCopyCUnion() || Record->isUnion()) Record->setHasNonTrivialToPrimitiveCopyCUnion(true); } + if (FD->hasAttr<ExplicitInitAttr>()) + Record->setHasUninitializedExplicitInitFields(true); if (FT.isDestructedType()) { Record->setNonTrivialToPrimitiveDestroy(true); Record->setParamDestroyedInCallee(true); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 0dd5f468cf60bf..b95cbbf4222056 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -264,6 +264,13 @@ static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT, updateStringLiteralType(Str, DeclT); } +void emitUninitializedExplicitInitFields(Sema &S, const RecordDecl *R) { + for (const FieldDecl *Field : R->fields()) { + if (Field->hasAttr<ExplicitInitAttr>()) + S.Diag(Field->getLocation(), diag::note_entity_declared_at) << Field; + } +} + //===----------------------------------------------------------------------===// // Semantic checking for initializer lists. //===----------------------------------------------------------------------===// @@ -738,6 +745,14 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, ILE->updateInit(SemaRef.Context, Init, Filler); return; } + + if (!VerifyOnly && Field->hasAttr<ExplicitInitAttr>()) { + SemaRef.Diag(ILE->getExprLoc(), diag::warn_field_requires_explicit_init) + << /* Var-in-Record */ 0 << Field; + SemaRef.Diag(Field->getLocation(), diag::note_entity_declared_at) + << Field; + } + // C++1y [dcl.init.aggr]p7: // If there are fewer initializer-clauses in the list than there are // members in the aggregate, then each member not explicitly initialized @@ -4558,6 +4573,14 @@ static void TryConstructorInitialization(Sema &S, CXXConstructorDecl *CtorDecl = cast<CXXConstructorDecl>(Best->Function); if (Result != OR_Deleted) { + if (!IsListInit && Kind.getKind() == InitializationKind::IK_Default && + DestRecordDecl != nullptr && DestRecordDecl->isAggregate() && + DestRecordDecl->hasUninitializedExplicitInitFields()) { + S.Diag(Kind.getLocation(), diag::warn_field_requires_explicit_init) + << /* Var-in-Record */ 1 << DestRecordDecl; + emitUninitializedExplicitInitFields(S, DestRecordDecl); + } + // C++11 [dcl.init]p6: // If a program calls for the default initialization of an object // of a const-qualified type T, T shall be a class type with a @@ -5852,6 +5875,12 @@ static void TryOrBuildParenListInitialization( } else { // We've processed all of the args, but there are still members that // have to be initialized. + if (!VerifyOnly && FD->hasAttr<ExplicitInitAttr>()) { + S.Diag(Kind.getLocation(), diag::warn_field_requires_explicit_init) + << /* Var-in-Record */ 0 << FD; + S.Diag(FD->getLocation(), diag::note_entity_declared_at) << FD; + } + if (FD->hasInClassInitializer()) { if (!VerifyOnly) { // C++ [dcl.init]p16.6.2.2 @@ -6457,6 +6486,19 @@ void InitializationSequence::InitializeFrom(Sema &S, } } + if (!S.getLangOpts().CPlusPlus && + Kind.getKind() == InitializationKind::IK_Default) { + RecordDecl *Rec = DestType->getAsRecordDecl(); + if (Rec && Rec->hasUninitializedExplicitInitFields()) { + VarDecl *Var = dyn_cast_or_null<VarDecl>(Entity.getDecl()); + if (Var && !Initializer) { + S.Diag(Var->getLocation(), diag::warn_field_requires_explicit_init) + << /* Var-in-Record */ 1 << Rec; + emitUninitializedExplicitInitFields(S, Rec); + } + } + } + // - If the destination type is a reference type, see 8.5.3. if (DestType->isReferenceType()) { // C++0x [dcl.init.ref]p1: @@ -7310,6 +7352,22 @@ PerformConstructorInitialization(Sema &S, if (S.DiagnoseUseOfDecl(Step.Function.FoundDecl, Loc)) return ExprError(); + if (Kind.getKind() == InitializationKind::IK_Value && + Constructor->isImplicit()) { + auto *RD = Step.Type.getCanonicalType()->getAsCXXRecordDecl(); + if (RD && RD->isAggregate() && RD->hasUninitializedExplicitInitFields()) { + unsigned I = 0; + for (const FieldDecl *FD : RD->fields()) { + if (I >= ConstructorArgs.size() && FD->hasAttr<ExplicitInitAttr>()) { + S.Diag(Loc, diag::warn_field_requires_explicit_init) + << /* Var-in-Record */ 0 << FD; + S.Diag(FD->getLocation(), diag::note_entity_declared_at) << FD; + } + ++I; + } + } + } + TypeSourceInfo *TSInfo = Entity.getTypeSourceInfo(); if (!TSInfo) TSInfo = S.Context.getTrivialTypeSourceInfo(Entity.getType(), Loc); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index ee72dd844b59a3..95abd75920c8fe 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -832,6 +832,7 @@ RedeclarableResult ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) { RecordDeclBits.getNextBit()); RD->setHasNonTrivialToPrimitiveDestructCUnion(RecordDeclBits.getNextBit()); RD->setHasNonTrivialToPrimitiveCopyCUnion(RecordDeclBits.getNextBit()); + RD->setHasUninitializedExplicitInitFields(RecordDeclBits.getNextBit()); RD->setParamDestroyedInCallee(RecordDeclBits.getNextBit()); RD->setArgPassingRestrictions( (RecordArgPassingKind)RecordDeclBits.getNextBits(/*Width=*/2)); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index f8ed155ca389d7..3b357f3c50dadb 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -611,6 +611,7 @@ void ASTDeclWriter::VisitRecordDecl(RecordDecl *D) { RecordDeclBits.addBit(D->hasNonTrivialToPrimitiveDefaultInitializeCUnion()); RecordDeclBits.addBit(D->hasNonTrivialToPrimitiveDestructCUnion()); RecordDeclBits.addBit(D->hasNonTrivialToPrimitiveCopyCUnion()); + RecordDeclBits.addBit(D->hasUninitializedExplicitInitFields()); RecordDeclBits.addBit(D->isParamDestroyedInCallee()); RecordDeclBits.addBits(llvm::to_underlying(D->getArgPassingRestrictions()), 2); Record.push_back(RecordDeclBits); @@ -2480,7 +2481,8 @@ void ASTWriter::WriteDeclAbbrevs() { // isNonTrivialToPrimitiveCopy, isNonTrivialToPrimitiveDestroy, // hasNonTrivialToPrimitiveDefaultInitializeCUnion, // hasNonTrivialToPrimitiveDestructCUnion, - // hasNonTrivialToPrimitiveCopyCUnion, isParamDestroyedInCallee, + // hasNonTrivialToPrimitiveCopyCUnion, + // hasUninitializedExplicitInitFields, isParamDestroyedInCallee, // getArgPassingRestrictions // ODRHash Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 26)); diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 4a6ac39da18ad2..55f196625770ab 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -79,6 +79,7 @@ // CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum) // CHECK-NEXT: Error (SubjectMatchRule_function) // CHECK-NEXT: ExcludeFromExplicitInstantiation (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record) +// CHECK-NEXT: ExplicitInit (SubjectMatchRule_field) // CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable)) // CHECK-NEXT: FlagEnum (SubjectMatchRule_enum) // CHECK-NEXT: Flatten (SubjectMatchRule_function) diff --git a/clang/test/Sema/uninit-variables.c b/clang/test/Sema/uninit-variables.c index 70a00793fd29ed..bad2e14bd28cb8 100644 --- a/clang/test/Sema/uninit-variables.c +++ b/clang/test/Sema/uninit-variables.c @@ -551,3 +551,33 @@ struct full_of_empty empty_test_2(void) { struct full_of_empty e; return e; // no-warning } + +struct with_explicit_field { + int x; + int y [[clang::requires_explicit_initialization]]; // #FIELD_Y +}; + +struct with_explicit_array { + [[clang::requires_explicit_initialization]] int arr[2]; // #FIELD_ARR +}; + +struct with_explicit_flex_array { + int x; + [[clang::requires_explicit_initialization]] int flex_arr[]; // #FIELD_FLEX_ARR +}; + +void aggregate() { + struct with_explicit_field a; // expected-warning {{field in 'with_explicit_field' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_Y {{'y' declared here}} + struct with_explicit_field b = {1}; // expected-warning {{field 'y' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_Y {{'y' declared here}} + (void)(&a != &b); + + struct with_explicit_field c = {1, 2}; + struct with_explicit_field d = {.y = 3}; + (void)(&c != &d); + + struct with_explicit_array e = {{1}}; // OK -- part of array is still initialized + (void)e; + + struct with_explicit_flex_array f = {2}; // expected-warning {{field 'flex_arr' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_FLEX_ARR {{'flex_arr' declared here}} + (void)f; +} diff --git a/clang/test/SemaCXX/uninitialized.cpp b/clang/test/SemaCXX/uninitialized.cpp index 8a640c9691b321..95dd488e81a624 100644 --- a/clang/test/SemaCXX/uninitialized.cpp +++ b/clang/test/SemaCXX/uninitialized.cpp @@ -1,5 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++1z -verify %s -// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++1z -verify %s -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -fsyntax-only -Wall -Wc++20-compat -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++1z -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wall -Wc++20-compat -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++1z -verify %s -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -fsyntax-only -Wall -Wc++20-compat -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++20 -verify %s // definitions for std::move namespace std { @@ -1472,3 +1473,175 @@ template<typename T> struct Outer { }; }; Outer<int>::Inner outerinner; + +struct Polymorphic { virtual ~Polymorphic() { } }; + +template<class... Bases> +struct Inherit : Bases... { // #TYPE_INHERIT + int g1; // #FIELD_G1 +}; + +template<class... Bases> +struct InheritWithExplicit : Bases... { // #TYPE_INHERIT_WITH_EXPLICIT + int g2 [[clang::requires_explicit_initialization]]; // #FIELD_G2 +}; + +struct Special {}; + +template<> +struct Inherit<Special> { + int g3 [[clang::requires_explicit_initialization]]; // #FIELD_G3 +}; + +template<> +struct InheritWithExplicit<Special> { + int g4; // #FIELD_G4 +}; + +void aggregate() { + struct NonAgg { + NonAgg() { } + [[clang::requires_explicit_initialization]] int na; // expected-warning {{'requires_explicit_initialization' attribute is ignored in non-aggregate type 'NonAgg'}} + }; + NonAgg nonagg; // no-warning + (void)nonagg; + + struct S { + [[clang::requires_explicit_initialization]] int s1; // #FIELD_S1 + int s2; + int s3 = 12; + [[clang::requires_explicit_initialization]] int s4 = 100; // #FIELD_S4 + static void foo(S) { } + }; + + struct C { +#if __cplusplus < 202002L + // expected-warning@+1 {{explicit initialization of field 'c1' will not be enforced in C++20 and later because 'C' has a user-declared constructor, making the type no longer an aggregate}} + [[clang::requires_explicit_initialization]] +#endif + int c1; // #FIELD_C1 + C() = default; // Test pre-C++20 aggregates + }; + + struct D : S { // #TYPE_D + int d1; + int d2 [[clang::requires_explicit_initialization]]; // #FIELD_D2 + }; + + struct D2 : D { // #TYPE_D2 + }; + + struct E { // #TYPE_E + int e1; + D e2 [[clang::requires_explicit_initialization]]; // #FIELD_E2 + struct { + [[clang::requires_explicit_initialization]] D e3; + D2 e4 [[clang::requires_explicit_initialization]]; + }; + }; + + S::foo(S{1, 2, 3, 4}); + S::foo(S{.s1 = 100, .s4 = 100}); + S::foo(S{.s1 = 100}); // expected-warning {{field 's4' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} + + S s{.s1 = 100, .s4 = 100}; + (void)s; + + S t{.s4 = 100}; // expected-warning {{field 's1' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S1 {{'s1' declared here}} + (void)t; + + S *ptr1 = new S; // expected-warning {{field in 'S' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} expected-note@#FIELD_S1 {{'s1' declared here}} + delete ptr1; + + S *ptr2 = new S{.s1 = 100, .s4 = 100}; + delete ptr2; + +#if __cplusplus >= 202002L + // expected-warning@+3 {{field 's1' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S1 {{'s1' declared here}} + // expected-warning@+2 {{field 's4' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} + // expected-warning@+1 {{field 'd2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_D2 {{'d2' declared here}} + D a1({}, 0); + (void)a1; + + // expected-warning@+3 {{field 's1' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S1 {{'s1' declared here}} + // expected-warning@+2 {{field 's4' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} + // expected-warning@+1 {{field 'd2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_D2 {{'d2' declared here}} + D a2(S{}, 0); + (void)a2; + + // expected-warning@+2 {{field 's4' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} + // expected-warning@+1 {{field 'd2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_D2 {{'d2' declared here}} + D a3(S{.s1 = 0}, 0); + (void)a3; + + // expected-warning@+3 {{field 's1' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S1 {{'s1' declared here}} + // expected-warning@+2 {{field 's4' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} + // expected-warning@+1 {{field 'd2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_D2 {{'d2' declared here}} + D a4(S(), 0); + (void)a4; + + // expected-warning@+2 {{field 's4' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} + // expected-warning@+1 {{field 'd2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_D2 {{'d2' declared here}} + D a5(S(0), 0); + (void)a5; + + // expected-warning@+2 {{field 's4' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} + // expected-warning@+1 {{field 'd2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_D2 {{'d2' declared here}} + D a6 = {S(0), 0}; + (void)a6; +#endif + +#if 201103L <= __cplusplus && __cplusplus < 202002L + C a; // expected-warning {{field in 'C' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_C1 {{'c1' declared here}} + (void)a; +#endif + + // expected-warning@+2 {{field 's4' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} + // expected-warning@+1 {{field 's1' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S1 {{'s1' declared here}} + D b{.d2 = 1}; + (void)b; + + // expected-warning@+3 {{field 's4' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} + // expected-warning@+2 {{field 'd2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_D2 {{'d2' declared here}} + // expected-warning@+1 {{field 's1' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S1 {{'s1' declared here}} + D c{.d1 = 5}; + + // expected-warning@+3 {{field 's4' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} + // expected-warning@+2 {{field 'd2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_D2 {{'d2' declared here}} + // expected-warning@+1 {{field 's1' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S1 {{'s1' declared here}} + c = {{}, 0}; + (void)c; + + // expected-note@+3 {{in implicit default constructor for 'D' first required here}} + // expected-warning@#TYPE_D {{field in 'S' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S1 {{'s1' declared here}} expected-note@#FIELD_S4 {{'s4' declared here}} + // expected-warning@+1 {{field in 'D' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_D2 {{'d2' declared here}} + D d; + (void)d; + + // expected-warning@+12 {{field in 'E' requires explicit initialization but is not explicitly initialized}} + // expected-note@#FIELD_E2 {{'e2' declared here}} + // expected-warning@#TYPE_E {{field in 'D' requires explicit initialization but is not explicitly initialized}} + // expected-note@+9 {{in implicit default constructor for 'E' first required here}} + // expected-note@#FIELD_D2 {{'d2' declared here}} + // expected-warning@#TYPE_E {{field in 'D' requires explicit initialization but is not explicitly initialized}} + // expected-note@#FIELD_D2 {{'d2' declared here}} + // expected-warning@#TYPE_E {{field in 'D2' requires explicit initialization but is not explicitly initialized}} + // expected-note@#TYPE_E {{in implicit default constructor for 'D2' first required here}} + // expected-warning@#TYPE_D2 {{field in 'D' requires explicit initialization but is not explicitly initialized}} + // expected-note@+2 {{in implicit default constructor for 'E' first required here}} + // expected-note@#FIELD_D2 {{'d2' declared here}} + E e; + (void)e; + + InheritWithExplicit<> agg; // expected-warning {{field in 'InheritWithExplicit<>' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_G2 {{'g2' declared here}} + (void)agg; + + InheritWithExplicit<Polymorphic> polymorphic; // expected-warning@#FIELD_G2 {{'requires_explicit_initialization' attribute is ignored in non-aggregate type 'InheritWithExplicit<Polymorphic>'}} + (void)polymorphic; + + Inherit<Special> specialized_explicit; // expected-warning {{field in 'Inherit<Special>' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_G3 {{'g3' declared here}} + (void)specialized_explicit; + + InheritWithExplicit<Special> specialized_implicit; // no-warning + (void)specialized_implicit; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits