[clang] [Clang] Add [[clang::complete_on_member_instantiation]] (PR #211231)
Nikolas Klauser via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 22 05:15:23 PDT 2026
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/211231
>From 1cee95814a28c5f67409bcb7a5c46b3155264aa0 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
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/include/clang/Basic/AttrDocs.td | 22 +++++++++
.../clang/Basic/DiagnosticSemaKinds.td | 3 ++
clang/lib/Sema/SemaDeclAttr.cpp | 15 ++++++
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 8 +++
...a-attribute-supported-attributes-list.test | 1 +
.../attr-complete-on-member-instantiation.cpp | 49 +++++++++++++++++++
7 files changed, 108 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..3555ccf3ce7ec 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 = [CompleteOnMemberInstDocs];
+ let InheritEvenIfAlreadyPresent = 1;
+ let TemplateDependent = 1;
+}
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 05e4cb0870652..05ca4209d7d4a 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -10266,3 +10266,25 @@ The attribute is also supported with blocks and in Objective-C.
}
}];
}
+
+def CompleteOnMemberInstDocs : Documentation {
+ let Category = DocCatDecl;
+ let Content = [{
+The ``complete_on_member_instantiation`` attribute can be applied to classes
+which require a type to be complete whenever a member function is called.
+Incomplete types are diagnosed at the point of the member function call.
+
+.. code-block:: c++
+
+ template <class T>
+ class [[clang::complete_on_member_instantiation(T)]] vector {
+ T* begin();
+ };
+
+ struct incomplete;
+
+ void example(vector<incomplete>& v) {
+ v.begin(); // this is guaranteed to produce an error
+ }
+ }];
+}
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/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 8bca68e2119e7..804399c967255 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -57,6 +57,7 @@
// CHECK-NEXT: CmseNSEntry (SubjectMatchRule_function)
// CHECK-NEXT: Cold (SubjectMatchRule_function)
// CHECK-NEXT: Common (SubjectMatchRule_variable)
+// CHECK-NEXT: CompleteOnMemberInst (SubjectMatchRule_record)
// CHECK-NEXT: Const (SubjectMatchRule_function)
// CHECK-NEXT: ConstInit (SubjectMatchRule_variable_is_global)
// CHECK-NEXT: Constructor (SubjectMatchRule_function)
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}}
+}
More information about the cfe-commits
mailing list