https://github.com/higher-performance created https://github.com/llvm/llvm-project/pull/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; }; >From e141508349c193f41b15c69a26f3096dd2ee1520 Mon Sep 17 00:00:00 2001 From: higher-performance <higher.performance.git...@gmail.com> Date: Mon, 5 Aug 2024 15:04:19 -0400 Subject: [PATCH] Add Clang attribute to ensure that fields are initialized explicitly --- .../clang/AST/CXXRecordDeclDefinitionBits.def | 8 +++++++ clang/include/clang/AST/DeclCXX.h | 5 +++++ clang/include/clang/Basic/Attr.td | 8 +++++++ clang/include/clang/Basic/AttrDocs.td | 22 +++++++++++++++++++ clang/include/clang/Basic/DiagnosticGroups.td | 1 + .../clang/Basic/DiagnosticSemaKinds.td | 3 +++ clang/lib/AST/DeclCXX.cpp | 9 ++++++++ clang/lib/Sema/SemaDeclAttr.cpp | 7 ++++++ clang/lib/Sema/SemaInit.cpp | 11 ++++++++++ clang/test/SemaCXX/uninitialized.cpp | 22 +++++++++++++++++++ 10 files changed, 96 insertions(+) diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def index cdf0804680ad0..a782026462566 100644 --- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def +++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def @@ -119,6 +119,14 @@ FIELD(HasInitMethod, 1, NO_MERGE) /// within anonymous unions or structs. FIELD(HasInClassInitializer, 1, NO_MERGE) +/// Custom attribute that is True if any field is marked as explicit in a type +/// without a user-provided default constructor, or if this is the case for any +/// base classes and/or member variables whose types are aggregates. +/// +/// In this case, default-construction is diagnosed, as it would not explicitly +/// initialize the field. +FIELD(HasUninitializedExplicitFields, 1, NO_MERGE) + /// True if any field is of reference type, and does not have an /// in-class initializer. /// diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index bf6a5ce92d438..8228595d84b0f 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1151,6 +1151,11 @@ class CXXRecordDecl : public RecordDecl { /// structs). bool hasInClassInitializer() const { return data().HasInClassInitializer; } + bool hasUninitializedExplicitFields() const { + return !isUnion() && !hasUserProvidedDefaultConstructor() && + data().HasUninitializedExplicitFields; + } + /// Whether this class or any of its subobjects has any members of /// reference type which would make value-initialization ill-formed. /// diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 8ac2079099c85..409a7cd9177e8 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1823,6 +1823,14 @@ def Leaf : InheritableAttr { let SimpleHandler = 1; } +def Explicit : InheritableAttr { + let Spellings = [Clang<"explicit", 0>]; + let Subjects = SubjectList<[Field], ErrorDiag>; + let Documentation = [ExplicitDocs]; + let LangOpts = [CPlusPlus]; + 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 94c284fc73158..66d0044aa979d 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1419,6 +1419,28 @@ is not specified. }]; } +def ExplicitDocs : Documentation { + let Category = DocCatField; + let Content = [{ +The ``clang::explicit`` attribute indicates that the field must be initialized +explicitly by the caller when the class is constructed. + +Example usage: + +.. code-block:: c++ + + struct some_aggregate { + int x; + int y [[clang::explicit]]; + }; + + some_aggregate create() { + return {.x = 1}; // error: y is not initialized explicitly + } + + }]; +} + def NoUniqueAddressDocs : Documentation { let Category = DocCatField; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 19c3f1e043349..52af3d3a7af39 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -787,6 +787,7 @@ def Trigraphs : DiagGroup<"trigraphs">; def UndefinedReinterpretCast : DiagGroup<"undefined-reinterpret-cast">; def ReinterpretBaseClass : DiagGroup<"reinterpret-base-class">; def Unicode : DiagGroup<"unicode">; +def UninitializedExplicit : DiagGroup<"uninitialized-explicit">; 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 581434d33c5c9..fb493e3c4f7f0 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2325,6 +2325,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_init : Warning< + "field %0 is left uninitialized, but was marked as requiring initialization">, + InGroup<UninitializedExplicit>; 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/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 9a3ede426e914..732117ae263c2 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -81,6 +81,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) HasPrivateFields(false), HasProtectedFields(false), HasPublicFields(false), HasMutableFields(false), HasVariantMembers(false), HasOnlyCMembers(true), HasInitMethod(false), HasInClassInitializer(false), + HasUninitializedExplicitFields(false), HasUninitializedReferenceMember(false), HasUninitializedFields(false), HasInheritedConstructor(false), HasInheritedDefaultConstructor(false), HasInheritedAssignment(false), @@ -1108,6 +1109,10 @@ void CXXRecordDecl::addedMember(Decl *D) { } else if (!T.isCXX98PODType(Context)) data().PlainOldData = false; + if (Field->hasAttr<ExplicitAttr>() && !Field->hasInClassInitializer()) { + data().HasUninitializedExplicitFields = true; + } + if (T->isReferenceType()) { if (!Field->hasInClassInitializer()) data().HasUninitializedReferenceMember = true; @@ -1359,6 +1364,10 @@ void CXXRecordDecl::addedMember(Decl *D) { if (!FieldRec->hasCopyAssignmentWithConstParam()) data().ImplicitCopyAssignmentHasConstParam = false; + if (FieldRec->hasUninitializedExplicitFields() && + FieldRec->isAggregate() && !Field->hasInClassInitializer()) + data().HasUninitializedExplicitFields = true; + if (FieldRec->hasUninitializedReferenceMember() && !Field->hasInClassInitializer()) data().HasUninitializedReferenceMember = true; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9011fa547638e..b55c845b74528 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -5943,6 +5943,10 @@ static void handleNoMergeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { D->addAttr(NoMergeAttr::Create(S.Context, AL)); } +static void handleExplicitAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + D->addAttr(ExplicitAttr::Create(S.Context, AL)); +} + static void handleNoUniqueAddressAttr(Sema &S, Decl *D, const ParsedAttr &AL) { D->addAttr(NoUniqueAddressAttr::Create(S.Context, AL)); } @@ -6848,6 +6852,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_NoMerge: handleNoMergeAttr(S, D, AL); break; + case ParsedAttr::AT_Explicit: + handleExplicitAttr(S, D, AL); + break; case ParsedAttr::AT_NoUniqueAddress: handleNoUniqueAddressAttr(S, D, AL); break; diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 90fd6df782f09..47b5c4bd388eb 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -743,6 +743,11 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, ILE->updateInit(SemaRef.Context, Init, Filler); return; } + + if (Field->hasAttr<ExplicitAttr>()) { + SemaRef.Diag(ILE->getExprLoc(), diag::warn_field_requires_init) << 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 @@ -4475,6 +4480,12 @@ 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->hasUninitializedExplicitFields()) { + S.Diag(Kind.getLocation(), diag::warn_field_requires_init) << "in class"; + } + // 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 diff --git a/clang/test/SemaCXX/uninitialized.cpp b/clang/test/SemaCXX/uninitialized.cpp index 8a640c9691b32..3a339b4f9c9c4 100644 --- a/clang/test/SemaCXX/uninitialized.cpp +++ b/clang/test/SemaCXX/uninitialized.cpp @@ -1472,3 +1472,25 @@ template<typename T> struct Outer { }; }; Outer<int>::Inner outerinner; + +void aggregate() { + struct B { + [[clang::explicit]] int f1; + }; + + struct S : B { // expected-warning {{uninitialized}} + int f2; + int f3 [[clang::explicit]]; + }; + +#if __cplusplus >= 202002L + S a({}, 0); // expected-warning {{'f1' is left uninitialized}} expected-warning {{'f3' is left uninitialized}} +#endif + S b{.f3 = 1}; // expected-warning {{'f1' is left uninitialized}} + S c{.f2 = 5}; // expected-warning {{'f1' is left uninitialized}} expected-warning {{'f3' is left uninitialized}} expected-warning {{'f3' is left uninitialized}} + c = {{}, 0}; // expected-warning {{'f1' is left uninitialized}} expected-warning {{'f3' is left uninitialized}} + S d; // expected-warning {{uninitialized}} expected-note {{constructor}} + (void)b; + (void)c; + (void)d; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits