-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[Clang][Sema] fix noexecpt mismatch of friend declaration #102267
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
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-clang Author: Qizhi Hu (jcsxky) ChangesCreating a new local scope without combining outer one will makes some instantiated template parameters can't be found in instantiate a friend function. This will lead to the mismatch with its definition. This patch fix this issue by adding instantiated template parameters to current scope. Full diff: https://github.com/llvm/llvm-project/pull/102267.diff 3 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 829a759ca4a0b..7b759cfd17119 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -192,6 +192,8 @@ Bug Fixes to C++ Support
- Clang now preserves the unexpanded flag in a lambda transform used for pack expansion. (#GH56852), (#GH85667),
(#GH99877).
- Fixed a bug when diagnosing ambiguous explicit specializations of constrained member functions.
+- Fix mismatch of noexecpt in friend function declaration by copying template instantiated parameters to
+ current local scope.(#GH101330).
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index f93cd113988ae..2ffea99a49378 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4698,7 +4698,22 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
// Enter the scope of this instantiation. We don't use
// PushDeclContext because we don't have a scope.
Sema::ContextRAII savedContext(*this, Decl);
+
+ FunctionDecl *Source = Proto->getExtProtoInfo().ExceptionSpec.SourceTemplate;
+ FunctionTemplateDecl *SourceTemplate = Source->getDescribedFunctionTemplate();
+ llvm::SmallDenseMap<clang::Decl *, clang::Decl *> InstTemplateParams;
+ if (CurrentInstantiationScope && SourceTemplate)
+ if (TemplateParameterList *TPL = SourceTemplate->getTemplateParameters())
+ for (NamedDecl *TemplateParam : *TPL)
+ if (auto *Found =
+ CurrentInstantiationScope->findInstantiationOf(TemplateParam))
+ if (auto *InstTemplateParam = Found->dyn_cast<clang::Decl *>())
+ InstTemplateParams[TemplateParam] = InstTemplateParam;
+
LocalInstantiationScope Scope(*this);
+ for (auto [TemplateParam, InstTemplateParam] : InstTemplateParams) {
+ Scope.InstantiatedLocal(TemplateParam, InstTemplateParam);
+ }
MultiLevelTemplateArgumentList TemplateArgs =
getTemplateInstantiationArgs(Decl, Decl->getLexicalDeclContext(),
diff --git a/clang/test/SemaCXX/pr101330.cpp b/clang/test/SemaCXX/pr101330.cpp
new file mode 100644
index 0000000000000..3d42bd5635562
--- /dev/null
+++ b/clang/test/SemaCXX/pr101330.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// expected-no-diagnostics
+
+template <typename T>
+struct C {
+ template <int N, typename U>
+ friend void func(const C<U> &m) noexcept(N == 0);
+};
+
+template <int N, typename U>
+void func(const C<U> &m) noexcept(N == 0) {}
+
+int main() {
+ C<int> t;
+ return 0;
+}
|
I don't think this is the right approach. I think we should compare the operands of the noexcept-specifier the same way we compare constraints (i.e. substitute so all references to template parameters have the correct depth and then check whether the substituted expressions are equivalent). WDYT @mizvekov? |
@sdkrystian Agree! The underlying issue is the incorrect depth makes the comparison of the operands of the |
@sdkrystian Could you please take another look at this patch? The approach does what you said before. template <typename T>
struct C {
template <int N>
friend void func() noexcept(N == 0);
};
template <int N>
void func() noexcept(N == 0) {}
int main() {
C<int> t;
return 0;
} Without this patch, |
gently ping~ |
nudge. i'd love to see #101330 fixed. |
@ericniebler Once I merge #106585, I have a follow up patch ready that will fix #101330. I'm at cppcon right now but I'll be back Thursday. |
…sts (#106585) Currently, clang rejects the following explicit specialization of `f` due to the constraints not being equivalent: ``` template<typename T> struct A { template<bool B> void f() requires B; }; template<> template<bool B> void A<int>::f() requires B { } ``` This happens because, in most cases, we do not set the flag indicating whether a `RedeclarableTemplate` is an explicit specialization of a member of an implicitly instantiated class template specialization until _after_ we compare constraints for equivalence. This patch addresses the issue (and a number of other issues) by: - storing the flag indicating whether a declaration is a member specialization on a per declaration basis, and - significantly refactoring `Sema::getTemplateInstantiationArgs` so we collect the right set of template argument in all cases. Many of our declaration matching & constraint evaluation woes can be traced back to bugs in `Sema::getTemplateInstantiationArgs`. This change/refactor should fix a lot of them. It also paves the way for fixing #101330 and #105462 per my suggestion in #102267 (which I have implemented on top of this patch but will merge in a subsequent PR).
for (NamedDecl *TemplateParam : *TPL) | ||
if (auto *Found = | ||
CurrentInstantiationScope->findInstantiationOf(TemplateParam)) | ||
if (auto *InstTemplateParam = Found->dyn_cast<clang::Decl *>()) | ||
InstTemplateParams[TemplateParam] = InstTemplateParam; | ||
|
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.
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.
Not that I’m aware of. I think we can wait for @sdkrystian‘s refactoring patch of MLTAL which might make such things easier.
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.
... including when checking constraints ...
This might not be the case for template parameters, as we (almost always, in my recollection) would have transformed the associated template parameters before calling the constraint matching function, so...
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.
Yeah, we should wait until I reland #106585 and then we can fix this. What we can then do is normalize the exception specification the same way we normalize constraints prior to comparing them.
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.
... the same way we normalize constraints prior to comparing them.
Which means we don't need an individual instantiation scope for the noexcept specification thereafter, so the question here could be resolved naturally I presume?
Creating a new local scope without combining outer one will makes some instantiated template parameters can't be found in instantiate a friend function. This will lead to the mismatch with its definition as they have template parameter with different depths. This patch fix this issue by adding instantiated template parameters to current scope. This will make clang find the correct instantiated template parameters and make the depth of template parameter correct.
Fix issue #101330