Skip to content

[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

Merged
merged 3 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ Attribute Changes in Clang
This allows the ``_Nullable`` and ``_Nonnull`` family of type attributes to
apply to this class.

- Clang now warns that the ``exclude_from_explicit_instantiation`` attribute
is ignored when applied to a local class or a member thereof.

Improvements to Clang's diagnostics
-----------------------------------
- Clang now applies syntax highlighting to the code snippets it
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">>;
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,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).
Expand Down Expand Up @@ -9339,6 +9354,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_Error:
handleErrorAttr(S, D, AL);
break;
case ParsedAttr::AT_ExcludeFromExplicitInstantiation:
Copy link
Collaborator

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.

Copy link
Member Author

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.

Copy link
Collaborator

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?

Copy link
Member Author

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))

handleExcludeFromExplicitInstantiationAttr(S, D, AL);
break;
case ParsedAttr::AT_DiagnoseIf:
handleDiagnoseIfAttr(S, D, AL);
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// RUN: %clang_cc1 -std=c++20 -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();
}
};

template<typename T>
auto f(T t) {
return A(t);
}

template<typename T>
auto g(T t) {
struct Local {
T value;

EXCLUDE_FROM_EXPLICIT_INSTANTIATION // expected-warning {{attribute ignored on local class member}}
operator A() {
return A(value);
}
};

return f(Local(t));
}

auto x = g(B());

}