Skip to content

Commit 652ae4e

Browse files
authored
[Clang][Sema] Warn when 'exclude_from_explicit_instantiation' attribute is used on local classes and members thereof (#88777)
A local class and its members declared in a function template are instantiated alongside the definition of that template. It therefore does not make sense to apply the `exclude_from_explicit_instantiation` attribute to such declarations, and this patch adds a warning to diagnose these cases (in addition to ignoring the attribute). (The motivation for this patch is to fix a failing test in libc++ for #84050. In particular, line 199 in `include/__memory/uses_allocator_construction.h` in libc++ contains the expression `this->__value_` (reduced to https://godbolt.org/z/KqEerKWPd) which will be looked up prior to instantiation once #84050 lands (the lookup context is the current instantiation). `_LIBCPP_HIDE_FROM_ABI` includes `__attribute__((exclude_from_explicit_instantiation))`, which in the reduced example results in `Local` being instantiated with `Local::operator A` as its `DeclContext`)
1 parent 2a4e61b commit 652ae4e

File tree

4 files changed

+88
-0
lines changed

4 files changed

+88
-0
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,9 @@ Attribute Changes in Clang
287287
This allows the ``_Nullable`` and ``_Nonnull`` family of type attributes to
288288
apply to this class.
289289

290+
- Clang now warns that the ``exclude_from_explicit_instantiation`` attribute
291+
is ignored when applied to a local class or a member thereof.
292+
290293
Improvements to Clang's diagnostics
291294
-----------------------------------
292295
- Clang now applies syntax highlighting to the code snippets it

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3669,6 +3669,9 @@ def warn_attribute_dllexport_explicit_instantiation_decl : Warning<
36693669
def warn_attribute_dllexport_explicit_instantiation_def : Warning<
36703670
"'dllexport' attribute ignored on explicit instantiation definition">,
36713671
InGroup<IgnoredAttributes>;
3672+
def warn_attribute_exclude_from_explicit_instantiation_local_class : Warning<
3673+
"%0 attribute ignored on local class%select{| member}1">,
3674+
InGroup<IgnoredAttributes>;
36723675
def warn_invalid_initializer_from_system_header : Warning<
36733676
"invalid constructor from class in system header, should not be explicit">,
36743677
InGroup<DiagGroup<"invalid-initializer-from-system-header">>;

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,21 @@ static void handleErrorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
984984
D->addAttr(EA);
985985
}
986986

987+
static void handleExcludeFromExplicitInstantiationAttr(Sema &S, Decl *D,
988+
const ParsedAttr &AL) {
989+
const auto *PD = isa<CXXRecordDecl>(D)
990+
? cast<DeclContext>(D)
991+
: D->getDeclContext()->getRedeclContext();
992+
if (const auto *RD = dyn_cast<CXXRecordDecl>(PD); RD && RD->isLocalClass()) {
993+
S.Diag(AL.getLoc(),
994+
diag::warn_attribute_exclude_from_explicit_instantiation_local_class)
995+
<< AL << /*IsMember=*/!isa<CXXRecordDecl>(D);
996+
return;
997+
}
998+
D->addAttr(::new (S.Context)
999+
ExcludeFromExplicitInstantiationAttr(S.Context, AL));
1000+
}
1001+
9871002
namespace {
9881003
/// Determines if a given Expr references any of the given function's
9891004
/// ParmVarDecls, or the function's implicit `this` parameter (if applicable).
@@ -9339,6 +9354,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
93399354
case ParsedAttr::AT_Error:
93409355
handleErrorAttr(S, D, AL);
93419356
break;
9357+
case ParsedAttr::AT_ExcludeFromExplicitInstantiation:
9358+
handleExcludeFromExplicitInstantiationAttr(S, D, AL);
9359+
break;
93429360
case ParsedAttr::AT_DiagnoseIf:
93439361
handleDiagnoseIfAttr(S, D, AL);
93449362
break;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
2+
3+
// Test that the exclude_from_explicit_instantiation attribute is ignored
4+
// for local classes and members thereof.
5+
6+
#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation)) // expected-note 0+{{expanded from macro}}
7+
8+
namespace N0 {
9+
10+
template<typename T>
11+
void f() {
12+
struct EXCLUDE_FROM_EXPLICIT_INSTANTIATION A { // expected-warning {{attribute ignored on local class}}
13+
// expected-note@-1 2{{in instantiation of}}
14+
EXCLUDE_FROM_EXPLICIT_INSTANTIATION void g(T t) { // expected-warning {{attribute ignored on local class member}}
15+
*t; // expected-error {{indirection requires pointer operand ('int' invalid)}}
16+
}
17+
18+
struct EXCLUDE_FROM_EXPLICIT_INSTANTIATION B { // expected-warning {{attribute ignored on local class}}
19+
void h(T t) {
20+
*t; // expected-error {{indirection requires pointer operand ('int' invalid)}}
21+
}
22+
};
23+
};
24+
}
25+
26+
template void f<int>(); // expected-note 2{{in instantiation of}}
27+
28+
}
29+
30+
// This is a reduced example from libc++ which required that 'value'
31+
// be prefixed with 'this->' because the definition of 'Local::operator A'
32+
// was not instantiated when the definition of 'g' was.
33+
namespace N1 {
34+
35+
struct A { };
36+
37+
struct B {
38+
operator A() {
39+
return A();
40+
}
41+
};
42+
43+
template<typename T>
44+
auto f(T t) {
45+
return A(t);
46+
}
47+
48+
template<typename T>
49+
auto g(T t) {
50+
struct Local {
51+
T value;
52+
53+
EXCLUDE_FROM_EXPLICIT_INSTANTIATION // expected-warning {{attribute ignored on local class member}}
54+
operator A() {
55+
return A(value);
56+
}
57+
};
58+
59+
return f(Local(t));
60+
}
61+
62+
auto x = g(B());
63+
64+
}

0 commit comments

Comments
 (0)