-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[Clang][Sema] Warn when 'exclude_from_explicit_instantiation' attribute is used on local classes and members thereof #88777
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang Author: Krystian Stasiowski (sdkrystian) ChangesA 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 (The motivation for this patch is to fix a failing test in libc++ for #84050. In particular, this line in libc++ contains the expression Full diff: https://github.com/llvm/llvm-project/pull/88777.diff 3 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5ec0218aedfe86..5385c634ca7c45 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3669,6 +3669,9 @@ def warn_attribute_dllexport_explicit_instantiation_decl : Warning<
def warn_attribute_dllexport_explicit_instantiation_def : Warning<
"'dllexport' attribute ignored on explicit instantiation definition">,
InGroup<IgnoredAttributes>;
+def warn_attribute_exclude_from_explicit_instantiation_local_class : Warning<
+ "%0 attribute ignored on local class%select{| member}1">,
+ InGroup<IgnoredAttributes>;
def warn_invalid_initializer_from_system_header : Warning<
"invalid constructor from class in system header, should not be explicit">,
InGroup<DiagGroup<"invalid-initializer-from-system-header">>;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index b7b1fbc625a150..dec9fc4cea234d 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -983,6 +983,21 @@ static void handleErrorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(EA);
}
+static void handleExcludeFromExplicitInstantiationAttr(Sema &S, Decl *D,
+ const ParsedAttr &AL) {
+ const auto *PD = isa<CXXRecordDecl>(D)
+ ? cast<DeclContext>(D)
+ : D->getDeclContext()->getRedeclContext();
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(PD); RD && RD->isLocalClass()) {
+ S.Diag(AL.getLoc(),
+ diag::warn_attribute_exclude_from_explicit_instantiation_local_class)
+ << AL << /*IsMember=*/!isa<CXXRecordDecl>(D);
+ return;
+ }
+ D->addAttr(::new (S.Context)
+ ExcludeFromExplicitInstantiationAttr(S.Context, AL));
+}
+
namespace {
/// Determines if a given Expr references any of the given function's
/// ParmVarDecls, or the function's implicit `this` parameter (if applicable).
@@ -9315,6 +9330,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_Error:
handleErrorAttr(S, D, AL);
break;
+ case ParsedAttr::AT_ExcludeFromExplicitInstantiation:
+ handleExcludeFromExplicitInstantiationAttr(S, D, AL);
+ break;
case ParsedAttr::AT_DiagnoseIf:
handleDiagnoseIfAttr(S, D, AL);
break;
diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.local-class.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.local-class.cpp
new file mode 100644
index 00000000000000..bac11d9cffbc23
--- /dev/null
+++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.local-class.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// Test that the exclude_from_explicit_instantiation attribute is ignored
+// for local classes and members thereof.
+
+#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation)) // expected-note 0+{{expanded from macro}}
+
+namespace N0 {
+
+template<typename T>
+void f() {
+ struct EXCLUDE_FROM_EXPLICIT_INSTANTIATION A { // expected-warning {{attribute ignored on local class}}
+ // expected-note@-1 2{{in instantiation of}}
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void g(T t) { // expected-warning {{attribute ignored on local class member}}
+ *t; // expected-error {{indirection requires pointer operand ('int' invalid)}}
+ }
+
+ struct EXCLUDE_FROM_EXPLICIT_INSTANTIATION B { // expected-warning {{attribute ignored on local class}}
+ void h(T t) {
+ *t; // expected-error {{indirection requires pointer operand ('int' invalid)}}
+ }
+ };
+ };
+}
+
+template void f<int>(); // expected-note 2{{in instantiation of}}
+
+}
+
+// This is a reduced example from libc++ which required that 'value'
+// be prefixed with 'this->' because the definition of 'Local::operator A'
+// was not instantiated when the definition of 'g' was.
+namespace N1 {
+
+ struct A { };
+
+ struct B {
+ operator A() {
+ return A();
+ }
+ };
+
+ auto f(auto&& x) {
+ return A(x);
+ }
+
+ template<typename T>
+ auto g(T x) {
+ struct Local {
+ T value;
+
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION // expected-warning {{attribute ignored on local class member}}
+ operator A() {
+ return A(value);
+ }
+ };
+
+ return f(Local(x));
+ }
+
+ auto x = g(B());
+
+}
|
bc39616
to
c4a31c7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a release note, probably int he 'potentially breaking changes' section?
@@ -9315,6 +9330,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, | |||
case ParsedAttr::AT_Error: | |||
handleErrorAttr(S, D, AL); | |||
break; | |||
case ParsedAttr::AT_ExcludeFromExplicitInstantiation: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the previous 'handle' function? Typically with decl attributes we need have an entry on this switch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exclude_from_explicit_instantiation
had no 'handle' function prior to this patch -- the only restriction was that it must be applied to the declaration of a variable, function, or class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It still needs to be 'added' to the AST though? Where was that happening?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the default case of the switch statement (line 9142, AL.getInfo().handleDeclAttribute(S, D, AL)
)
@erichkeane Perhaps the release note should go under "Attribute Changes in Clang"? |
That would be fine for me as well. |
…for members of local classes
c4a31c7
to
72fec49
Compare
72fec49
to
2cb7815
Compare
Release note added |
I noticed that when compiling libcxx with this patch there are zillions of warnings like
Is there any plan to clean this out? |
@mikaelholmen I believe there are two uses of |
Nice! I have no idea about this I just saw the warnings when I compiled and connected them to this patch. |
@mikaelholmen See #89377 |
… on local class members (llvm#89377) llvm#88777 adds a warning for when the `exclude_from_explicit_instantiation` attribute is applied to local classes and members thereof. This patch addresses the few instances of `exclude_from_explicit_instantiation` being applied to local class members in libc++.
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, this line in libc++ contains the expression
this->__value_
(reduced repro here) which will be looked up prior to instantiation (the lookup context is the current instantiation) once #84050 lands._LIBCPP_HIDE_FROM_ABI
includes__attribute__((exclude_from_explicit_instantiation))
, which TLDR in the reduced repro results inLocal
being instantiated withLocal::operator A
as itsDeclContext
)