Skip to content

Commit 1695451

Browse files
Add Clang attribute to ensure that fields are initialized explicitly
1 parent a9ffb71 commit 1695451

File tree

11 files changed

+99
-0
lines changed

11 files changed

+99
-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(HasUninitializedExplicitInitFields, 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
@@ -1152,6 +1152,11 @@ class CXXRecordDecl : public RecordDecl {
11521152
/// structs).
11531153
bool hasInClassInitializer() const { return data().HasInClassInitializer; }
11541154

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

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 ExplicitInit : InheritableAttr {
1827+
let Spellings = [Clang<"requires_explicit_initialization", 0>];
1828+
let Subjects = SubjectList<[Field], ErrorDiag>;
1829+
let Documentation = [ExplicitInitDocs];
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: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,29 @@ is not specified.
14191419
}];
14201420
}
14211421

1422+
def ExplicitInitDocs : Documentation {
1423+
let Category = DocCatField;
1424+
let Content = [{
1425+
The ``clang::requires_explicit_initialization`` attribute indicates that the
1426+
field of an aggregate must be initialized explicitly by the caller when the
1427+
class is constructed. Its usage is invalid on non-aggregates.
1428+
1429+
Example usage:
1430+
1431+
.. code-block:: c++
1432+
1433+
struct some_aggregate {
1434+
int x;
1435+
int y [[clang::requires_explicit_initialization]];
1436+
};
1437+
1438+
some_aggregate create() {
1439+
return {.x = 1}; // error: y is not initialized explicitly
1440+
}
1441+
1442+
}];
1443+
}
1444+
14221445
def NoUniqueAddressDocs : Documentation {
14231446
let Category = DocCatField;
14241447
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 UninitializedExplicitInit : DiagGroup<"uninitialized-explicit-init">;
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
@@ -2332,6 +2332,9 @@ def err_init_reference_member_uninitialized : Error<
23322332
"reference member of type %0 uninitialized">;
23332333
def note_uninit_reference_member : Note<
23342334
"uninitialized reference member is here">;
2335+
def warn_field_requires_explicit_init : Warning<
2336+
"field %0 is not explicitly initialized, but was marked as requiring "
2337+
"explicit initialization">, InGroup<UninitializedExplicitInit>;
23352338
def warn_field_is_uninit : Warning<"field %0 is uninitialized when used here">,
23362339
InGroup<Uninitialized>;
23372340
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+
HasUninitializedExplicitInitFields(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<ExplicitInitAttr>() && !Field->hasInClassInitializer()) {
1113+
data().HasUninitializedExplicitInitFields = 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->hasUninitializedExplicitInitFields() &&
1368+
FieldRec->isAggregate() && !Field->hasInClassInitializer())
1369+
data().HasUninitializedExplicitInitFields = 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
@@ -5942,6 +5942,10 @@ static void handleNoMergeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
59425942
D->addAttr(NoMergeAttr::Create(S.Context, AL));
59435943
}
59445944

5945+
static void handleExplicitInitAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
5946+
D->addAttr(ExplicitInitAttr::Create(S.Context, AL));
5947+
}
5948+
59455949
static void handleNoUniqueAddressAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
59465950
D->addAttr(NoUniqueAddressAttr::Create(S.Context, AL));
59475951
}
@@ -6847,6 +6851,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
68476851
case ParsedAttr::AT_NoMerge:
68486852
handleNoMergeAttr(S, D, AL);
68496853
break;
6854+
case ParsedAttr::AT_ExplicitInit:
6855+
handleExplicitInitAttr(S, D, AL);
6856+
break;
68506857
case ParsedAttr::AT_NoUniqueAddress:
68516858
handleNoUniqueAddressAttr(S, D, AL);
68526859
break;

clang/lib/Sema/SemaInit.cpp

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

45494555
CXXConstructorDecl *CtorDecl = cast<CXXConstructorDecl>(Best->Function);
45504556
if (Result != OR_Deleted) {
4557+
if (!IsListInit && Kind.getKind() == InitializationKind::IK_Default &&
4558+
DestRecordDecl != nullptr && DestRecordDecl->isAggregate() &&
4559+
DestRecordDecl->hasUninitializedExplicitInitFields()) {
4560+
S.Diag(Kind.getLocation(), diag::warn_field_requires_explicit_init) << "in class";
4561+
}
4562+
45514563
// C++11 [dcl.init]p6:
45524564
// If a program calls for the default initialization of an object
45534565
// 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
@@ -76,6 +76,7 @@
7676
// CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum)
7777
// CHECK-NEXT: Error (SubjectMatchRule_function)
7878
// CHECK-NEXT: ExcludeFromExplicitInstantiation (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
79+
// CHECK-NEXT: ExplicitInit (SubjectMatchRule_field)
7980
// 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))
8081
// CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
8182
// CHECK-NEXT: Flatten (SubjectMatchRule_function)

0 commit comments

Comments
 (0)