diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3960,7 +3960,7 @@ def Uninitialized : InheritableAttr { let Spellings = [Clang<"uninitialized", 0>]; - let Subjects = SubjectList<[LocalVar]>; + let Subjects = SubjectList<[LocalVar, Record]>; let PragmaAttributeSupport = 1; let Documentation = [UninitializedDocs]; } diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -5782,8 +5782,10 @@ The command-line parameter ``-ftrivial-auto-var-init=*`` can be used to initialize trivial automatic stack variables. By default, trivial automatic stack variables are uninitialized. This attribute is used to override the -command-line parameter, forcing variables to remain uninitialized. It has no -semantic meaning in that using uninitialized values is undefined behavior, +command-line parameter, forcing variables to remain uninitialized. +When set on a struct or class, all stack variables of this type are affected. + +It has no semantic meaning in that using uninitialized values is undefined behavior, it rather documents the programmer's intent. }]; } diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -1902,13 +1902,17 @@ const Address Loc = locIsByrefHeader ? emission.getObjectAddress(*this) : emission.Addr; - // Note: constexpr already initializes everything correctly. - LangOptions::TrivialAutoVarInitKind trivialAutoVarInit = - (D.isConstexpr() - ? LangOptions::TrivialAutoVarInitKind::Uninitialized - : (D.getAttr() - ? LangOptions::TrivialAutoVarInitKind::Uninitialized - : getContext().getLangOpts().getTrivialAutoVarInit())); + LangOptions::TrivialAutoVarInitKind trivialAutoVarInit; + if (D.isConstexpr()) + // Note: constexpr already initializes everything correctly. + trivialAutoVarInit = LangOptions::TrivialAutoVarInitKind::Uninitialized; + else if (D.getAttr()) + trivialAutoVarInit = LangOptions::TrivialAutoVarInitKind::Uninitialized; + else if (const auto *RecordTy = D.getType()->getAsRecordDecl(); + RecordTy && RecordTy->hasAttr()) + trivialAutoVarInit = LangOptions::TrivialAutoVarInitKind::Uninitialized; + else + trivialAutoVarInit = getContext().getLangOpts().getTrivialAutoVarInit(); auto initializeWhatIsTechnicallyUninitialized = [&](Address Loc) { if (trivialAutoVarInit == diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8438,8 +8438,11 @@ } static void handleUninitializedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - assert(cast(D)->getStorageDuration() == SD_Automatic && - "uninitialized is only valid on automatic duration variables"); + assert(((isa(D) && + cast(D)->getStorageDuration() == SD_Automatic) || + isa(D)) && + "uninitialized is only valid on automatic duration variables and " + "record type"); D->addAttr(::new (S.Context) UninitializedAttr(S.Context, AL)); } diff --git a/clang/test/CodeGenCXX/trivial-auto-var-init-attribute.cpp b/clang/test/CodeGenCXX/trivial-auto-var-init-attribute.cpp --- a/clang/test/CodeGenCXX/trivial-auto-var-init-attribute.cpp +++ b/clang/test/CodeGenCXX/trivial-auto-var-init-attribute.cpp @@ -38,4 +38,24 @@ } #pragma clang attribute pop +struct [[clang::uninitialized]] uninitialized_record { + int i; +}; + +// UNINIT-LABEL: test_record_attribute_uninitialized( +// UNINIT: alloca +// UNINIT-NEXT: call void +// ZERO-LABEL: test_record_attribute_uninitialized( +// ZERO: alloca +// ZERO-NOT: !annotation +// ZERO-NEXT: call void +// PATTERN-LABEL: test_record_attribute_uninitialized( +// PATTERN: alloca +// PATTERN-NOT: !annotation +// PATTERN-NEXT: call void +void test_record_attribute_uninitialized() { + uninitialized_record some; + used(some); +} + } // extern "C" diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -188,7 +188,7 @@ // CHECK-NEXT: TargetVersion (SubjectMatchRule_function) // CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member) // CHECK-NEXT: TrivialABI (SubjectMatchRule_record) -// CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local) +// CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local, SubjectMatchRule_record) // CHECK-NEXT: UnsafeBufferUsage (SubjectMatchRule_function) // CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: VecReturn (SubjectMatchRule_record) diff --git a/clang/test/Sema/attr-uninitialized.c b/clang/test/Sema/attr-uninitialized.c --- a/clang/test/Sema/attr-uninitialized.c +++ b/clang/test/Sema/attr-uninitialized.c @@ -18,4 +18,4 @@ struct TheWordIsOut { __attribute((uninitialized)) int youre_doin_wrong; // expected-warning {{'uninitialized' attribute only applies to local variables}} -} __attribute((uninitialized)); // expected-warning {{'uninitialized' attribute only applies to local variables}} +} __attribute((uninitialized));