[clang] [Clang] Add [[clang::complete_on_member_instantiation]] (PR #211231)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 22 04:03:52 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Nikolas Klauser (philnik777)
<details>
<summary>Changes</summary>
This attribute ensures that the provided type is complete when any of the member functions of a class are instantiated. This allows easily enforcing this invariant across an entire class (e.g. `std::vector`) without having to `static_assert(sizeof(T) >= 0)` in every single member function. It also improves error messages, since the call site is diagnosed instead of something potentially deep within the function instantiation.
---
Full diff: https://github.com/llvm/llvm-project/pull/211231.diff
5 Files Affected:
- (modified) clang/include/clang/Basic/Attr.td (+10-1)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3)
- (modified) clang/lib/Sema/SemaDeclAttr.cpp (+15)
- (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+8)
- (added) clang/test/SemaCXX/attr-complete-on-member-instantiation.cpp (+49)
``````````diff
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 1a5bd2301dfc8..fdead2eba2492 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5316,7 +5316,7 @@ def HLSLVkLocation : HLSLAnnotationAttr {
}
// `row_major` / `column_major` are HLSL keywords that select the in-memory
-// layout of a matrix-typed declaration.
+// layout of a matrix-typed declaration.
def HLSLRowMajor : TypeAttr {
let Spellings = [CustomKeyword<"row_major">];
let LangOpts = [HLSL];
@@ -5503,3 +5503,12 @@ def Personality : InheritableAttr {
let Subjects = SubjectList<[Function]>;
let Documentation = [PersonalityDocs];
}
+
+def CompleteOnMemberInst : InheritableAttr {
+ let Spellings = [Clang<"complete_on_member_instantiation">];
+ let Args = [TypeArgument<"CompleteType">];
+ let Subjects = SubjectList<[CXXRecord]>;
+ let Documentation = [Undocumented];
+ let InheritEvenIfAlreadyPresent = 1;
+ let TemplateDependent = 1;
+}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3ff7e30d9f1f7..5e2b82545f907 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2182,6 +2182,9 @@ def ext_out_of_line_qualified_id_type_names_constructor : ExtWarn<
"%select{'typename'|'template'}2 keyword">, SFINAEFailure,
InGroup<DiagGroup<"injected-class-name">>;
+def err_incomplete_type_on_member_inst : Error<
+ "%0 has to be complete when calling a member function">;
+
// C++ class members
def err_storageclass_invalid_for_member : Error<
"storage class specified for a member declaration">;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 1b272b5416860..59bc09e471075 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2071,6 +2071,18 @@ static void handleCommonAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(::new (S.Context) CommonAttr(S.Context, AL));
}
+static void handleCompleteOnMemberInstAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ if (!AL.hasParsedType()) {
+ S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1;
+ return;
+ }
+
+ TypeSourceInfo *ParmTSI = nullptr;
+ S.GetTypeFromParser(AL.getTypeArg(), &ParmTSI);
+ assert(ParmTSI && "no type source info for attribute argument");
+ D->addAttr(CompleteOnMemberInstAttr::Create(S.Context, ParmTSI, AL));
+}
+
static void handleNakedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (AL.isDeclspecAttribute()) {
const auto &Triple = S.getASTContext().getTargetInfo().getTriple();
@@ -7787,6 +7799,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_Common:
handleCommonAttr(S, D, AL);
break;
+ case ParsedAttr::AT_CompleteOnMemberInst:
+ handleCompleteOnMemberInstAttr(S, D, AL);
+ break;
case ParsedAttr::AT_CUDAConstant:
handleConstantAttr(S, D, AL);
break;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c2e90624b8a6e..0776144f8826e 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5766,6 +5766,14 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
}
}
+ if (auto *RD = dyn_cast<CXXRecordDecl>(Function->getDeclContext())) {
+ for (auto *COMI : RD->specific_attrs<CompleteOnMemberInstAttr>()) {
+ if (RequireCompleteType(PointOfInstantiation, COMI->getCompleteType(),
+ diag::err_incomplete_type_on_member_inst))
+ return;
+ }
+ }
+
NonSFINAEContext _(*this);
InstantiatingTemplate Inst(*this, PointOfInstantiation, Function);
if (Inst.isInvalid())
diff --git a/clang/test/SemaCXX/attr-complete-on-member-instantiation.cpp b/clang/test/SemaCXX/attr-complete-on-member-instantiation.cpp
new file mode 100644
index 0000000000000..3b4de2963bb27
--- /dev/null
+++ b/clang/test/SemaCXX/attr-complete-on-member-instantiation.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 %s -verify -fsyntax-only
+
+template <class T>
+struct [[clang::complete_on_member_instantiation]] S1 {}; // expected-error {{'clang::complete_on_member_instantiation' attribute takes one argument}}
+
+template <class T>
+struct [[clang::complete_on_member_instantiation(1)]] S2 {}; // expected-error {{expected a type}}
+
+template <class T>
+struct [[clang::complete_on_member_instantiation(T)]] RequiresComplete {
+ void func() {}
+};
+
+struct Complete {};
+struct Incomplete; // expected-note 7 {{forward declaration of 'Incomplete'}}
+
+void func(RequiresComplete<Incomplete>& incomplete, RequiresComplete<Complete>& complete) {
+ complete.func();
+ incomplete.func(); // expected-error {{'Incomplete' has to be complete when calling a member function}}
+}
+
+template <class T, class U>
+struct [[clang::complete_on_member_instantiation(T), clang::complete_on_member_instantiation(U)]] RequiresComplete2 {
+ void func() {}
+};
+
+void func2(RequiresComplete2<Incomplete, Incomplete> both_incomplete,
+ RequiresComplete2<Incomplete, Complete> first_incomplete,
+ RequiresComplete2<Complete, Incomplete> second_incomplete) {
+ both_incomplete.func(); // expected-error {{'Incomplete' has to be complete when calling a member function}}
+ first_incomplete.func(); // expected-error {{'Incomplete' has to be complete when calling a member function}}
+ second_incomplete.func(); // expected-error {{'Incomplete' has to be complete when calling a member function}}
+}
+
+template <class T, class U>
+struct [[clang::complete_on_member_instantiation(T), clang::complete_on_member_instantiation(U)]] RequiresComplete3;
+
+template <class T, class U>
+struct RequiresComplete3 {
+ void func() {}
+};
+
+void func2(RequiresComplete3<Incomplete, Incomplete> both_incomplete,
+ RequiresComplete3<Incomplete, Complete> first_incomplete,
+ RequiresComplete3<Complete, Incomplete> second_incomplete) {
+ both_incomplete.func(); // expected-error {{'Incomplete' has to be complete when calling a member function}}
+ first_incomplete.func(); // expected-error {{'Incomplete' has to be complete when calling a member function}}
+ second_incomplete.func(); // expected-error {{'Incomplete' has to be complete when calling a member function}}
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/211231
More information about the cfe-commits
mailing list