Skip to content

Commit f09b427

Browse files
Add Clang attribute to ensure that fields are initialized explicitly
1 parent ddb9869 commit f09b427

File tree

11 files changed

+97
-0
lines changed

11 files changed

+97
-0
lines changed

clang/include/clang/AST/CXXRecordDeclDefinitionBits.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ FIELD(HasInitMethod, 1, NO_MERGE)
119119
/// within anonymous unions or structs.
120120
FIELD(HasInClassInitializer, 1, NO_MERGE)
121121

122+
/// Custom attribute that is True if any field is marked as explicit in a type
123+
/// without a user-provided default constructor, or if this is the case for any
124+
/// base classes and/or member variables whose types are aggregates.
125+
///
126+
/// In this case, default-construction is diagnosed, as it would not explicitly
127+
/// initialize the field.
128+
FIELD(HasUninitializedExplicitFields, 1, NO_MERGE)
129+
122130
/// True if any field is of reference type, and does not have an
123131
/// in-class initializer.
124132
///

clang/include/clang/AST/DeclCXX.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,11 @@ class CXXRecordDecl : public RecordDecl {
11511151
/// structs).
11521152
bool hasInClassInitializer() const { return data().HasInClassInitializer; }
11531153

1154+
bool hasUninitializedExplicitFields() const {
1155+
return !isUnion() && !hasUserProvidedDefaultConstructor() &&
1156+
data().HasUninitializedExplicitFields;
1157+
}
1158+
11541159
/// Whether this class or any of its subobjects has any members of
11551160
/// reference type which would make value-initialization ill-formed.
11561161
///

clang/include/clang/Basic/Attr.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,6 +1823,14 @@ def Leaf : InheritableAttr {
18231823
let SimpleHandler = 1;
18241824
}
18251825

1826+
def Explicit : InheritableAttr {
1827+
let Spellings = [Clang<"explicit", 0>];
1828+
let Subjects = SubjectList<[Field], ErrorDiag>;
1829+
let Documentation = [ExplicitDocs];
1830+
let LangOpts = [CPlusPlus];
1831+
let SimpleHandler = 1;
1832+
}
1833+
18261834
def LifetimeBound : DeclOrTypeAttr {
18271835
let Spellings = [Clang<"lifetimebound", 0>];
18281836
let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,28 @@ is not specified.
14191419
}];
14201420
}
14211421

1422+
def ExplicitDocs : Documentation {
1423+
let Category = DocCatField;
1424+
let Content = [{
1425+
The ``clang::explicit`` attribute indicates that the field must be initialized
1426+
explicitly by the caller when the class is constructed.
1427+
1428+
Example usage:
1429+
1430+
.. code-block:: c++
1431+
1432+
struct some_aggregate {
1433+
int x;
1434+
int y [[clang::explicit]];
1435+
};
1436+
1437+
some_aggregate create() {
1438+
return {.x = 1}; // error: y is not initialized explicitly
1439+
}
1440+
1441+
}];
1442+
}
1443+
14221444
def NoUniqueAddressDocs : Documentation {
14231445
let Category = DocCatField;
14241446
let Content = [{

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,7 @@ def Trigraphs : DiagGroup<"trigraphs">;
787787
def UndefinedReinterpretCast : DiagGroup<"undefined-reinterpret-cast">;
788788
def ReinterpretBaseClass : DiagGroup<"reinterpret-base-class">;
789789
def Unicode : DiagGroup<"unicode">;
790+
def UninitializedExplicit : DiagGroup<"uninitialized-explicit">;
790791
def UninitializedMaybe : DiagGroup<"conditional-uninitialized">;
791792
def UninitializedSometimes : DiagGroup<"sometimes-uninitialized">;
792793
def UninitializedStaticSelfInit : DiagGroup<"static-self-init">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,6 +2325,9 @@ def err_init_reference_member_uninitialized : Error<
23252325
"reference member of type %0 uninitialized">;
23262326
def note_uninit_reference_member : Note<
23272327
"uninitialized reference member is here">;
2328+
def warn_field_requires_init : Warning<
2329+
"field %0 is left uninitialized, but was marked as requiring initialization">,
2330+
InGroup<UninitializedExplicit>;
23282331
def warn_field_is_uninit : Warning<"field %0 is uninitialized when used here">,
23292332
InGroup<Uninitialized>;
23302333
def warn_base_class_is_uninit : Warning<

clang/lib/AST/DeclCXX.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
8181
HasPrivateFields(false), HasProtectedFields(false),
8282
HasPublicFields(false), HasMutableFields(false), HasVariantMembers(false),
8383
HasOnlyCMembers(true), HasInitMethod(false), HasInClassInitializer(false),
84+
HasUninitializedExplicitFields(false),
8485
HasUninitializedReferenceMember(false), HasUninitializedFields(false),
8586
HasInheritedConstructor(false), HasInheritedDefaultConstructor(false),
8687
HasInheritedAssignment(false),
@@ -1108,6 +1109,10 @@ void CXXRecordDecl::addedMember(Decl *D) {
11081109
} else if (!T.isCXX98PODType(Context))
11091110
data().PlainOldData = false;
11101111

1112+
if (Field->hasAttr<ExplicitAttr>() && !Field->hasInClassInitializer()) {
1113+
data().HasUninitializedExplicitFields = true;
1114+
}
1115+
11111116
if (T->isReferenceType()) {
11121117
if (!Field->hasInClassInitializer())
11131118
data().HasUninitializedReferenceMember = true;
@@ -1359,6 +1364,10 @@ void CXXRecordDecl::addedMember(Decl *D) {
13591364
if (!FieldRec->hasCopyAssignmentWithConstParam())
13601365
data().ImplicitCopyAssignmentHasConstParam = false;
13611366

1367+
if (FieldRec->hasUninitializedExplicitFields() &&
1368+
FieldRec->isAggregate() && !Field->hasInClassInitializer())
1369+
data().HasUninitializedExplicitFields = true;
1370+
13621371
if (FieldRec->hasUninitializedReferenceMember() &&
13631372
!Field->hasInClassInitializer())
13641373
data().HasUninitializedReferenceMember = true;

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5943,6 +5943,10 @@ static void handleNoMergeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
59435943
D->addAttr(NoMergeAttr::Create(S.Context, AL));
59445944
}
59455945

5946+
static void handleExplicitAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
5947+
D->addAttr(ExplicitAttr::Create(S.Context, AL));
5948+
}
5949+
59465950
static void handleNoUniqueAddressAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
59475951
D->addAttr(NoUniqueAddressAttr::Create(S.Context, AL));
59485952
}
@@ -6848,6 +6852,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
68486852
case ParsedAttr::AT_NoMerge:
68496853
handleNoMergeAttr(S, D, AL);
68506854
break;
6855+
case ParsedAttr::AT_Explicit:
6856+
handleExplicitAttr(S, D, AL);
6857+
break;
68516858
case ParsedAttr::AT_NoUniqueAddress:
68526859
handleNoUniqueAddressAttr(S, D, AL);
68536860
break;

clang/lib/Sema/SemaInit.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,11 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
743743
ILE->updateInit(SemaRef.Context, Init, Filler);
744744
return;
745745
}
746+
747+
if (Field->hasAttr<ExplicitAttr>()) {
748+
SemaRef.Diag(ILE->getExprLoc(), diag::warn_field_requires_init) << Field;
749+
}
750+
746751
// C++1y [dcl.init.aggr]p7:
747752
// If there are fewer initializer-clauses in the list than there are
748753
// members in the aggregate, then each member not explicitly initialized
@@ -4475,6 +4480,12 @@ static void TryConstructorInitialization(Sema &S,
44754480

44764481
CXXConstructorDecl *CtorDecl = cast<CXXConstructorDecl>(Best->Function);
44774482
if (Result != OR_Deleted) {
4483+
if (!IsListInit && Kind.getKind() == InitializationKind::IK_Default &&
4484+
DestRecordDecl != nullptr && DestRecordDecl->isAggregate() &&
4485+
DestRecordDecl->hasUninitializedExplicitFields()) {
4486+
S.Diag(Kind.getLocation(), diag::warn_field_requires_init) << "in class";
4487+
}
4488+
44784489
// C++11 [dcl.init]p6:
44794490
// If a program calls for the default initialization of an object
44804491
// of a const-qualified type T, T shall be a class type with a

clang/test/Misc/pragma-attribute-supported-attributes-list.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
// CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum)
7676
// CHECK-NEXT: Error (SubjectMatchRule_function)
7777
// CHECK-NEXT: ExcludeFromExplicitInstantiation (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
78+
// CHECK-NEXT: Explicit (SubjectMatchRule_field)
7879
// 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))
7980
// CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
8081
// CHECK-NEXT: Flatten (SubjectMatchRule_function)

0 commit comments

Comments
 (0)