https://github.com/philnik777 created 
https://github.com/llvm/llvm-project/pull/211231

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.


>From 696d3c098bb9d2d515eebbbe46a9294149f3e599 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <[email protected]>
Date: Wed, 22 Jul 2026 12:58:27 +0200
Subject: [PATCH] [Clang] Add [[clang::complete_on_member_instantiation]]

---
 clang/include/clang/Basic/Attr.td             | 11 ++++-
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 ++
 clang/lib/Sema/SemaDeclAttr.cpp               | 15 ++++++
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  8 +++
 .../attr-complete-on-member-instantiation.cpp | 49 +++++++++++++++++++
 5 files changed, 85 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/SemaCXX/attr-complete-on-member-instantiation.cpp

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}}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to