-
Notifications
You must be signed in to change notification settings - Fork 12k
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
[clang][Sema] Skip the RequiresExprBodyDecls for lambda dependencies #83997
Conversation
@llvm/pr-subscribers-clang Author: Younan Zhang (zyn0217) ChangesThe dependency of a lambda inside of a This fixes #56556, #82849 and a case demonstrated by #49570 (comment). Full diff: https://github.com/llvm/llvm-project/pull/83997.diff 3 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5e0352a7eaf6cd..9c64cd2b5f6a10 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -311,6 +311,8 @@ Bug Fixes to C++ Support
Fixes (#GH80630)
- Fix a crash when an explicit template argument list is used with a name for which lookup
finds a non-template function and a dependent using declarator.
+- Fixed an issue where the ``RequiresExprBody`` was involved in the lambda dependency
+ calculation. (#GH56556), (#GH82849).
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 7389a48fe56fcc..84348e13567e71 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -13649,10 +13649,29 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
// use evaluation contexts to distinguish the function parameter case.
CXXRecordDecl::LambdaDependencyKind DependencyKind =
CXXRecordDecl::LDK_Unknown;
+ DeclContext *DC = getSema().CurContext;
+ // A RequiresExprBodyDecl is not interesting for dependencies.
+ // For the following case,
+ //
+ // template <typename>
+ // concept C = requires { [] {}; };
+ //
+ // template <class F>
+ // struct Widget;
+ //
+ // template <C F>
+ // struct Widget<F> {};
+ //
+ // While we are here in substitution for Widget<F>, the parent of DC would be
+ // the template specialization itself. Thus, the lambda expression
+ // will be deemed as dependent even if we have non-dependent template
+ // arguments.
+ // (A ClassTemplateSpecializationDecl is always a dependent context.)
+ if (DC->getDeclKind() == Decl::Kind::RequiresExprBody)
+ DC = DC->getParent();
if ((getSema().isUnevaluatedContext() ||
getSema().isConstantEvaluatedContext()) &&
- (getSema().CurContext->isFileContext() ||
- !getSema().CurContext->getParent()->isDependentContext()))
+ (DC->isFileContext() || !DC->getParent()->isDependentContext()))
DependencyKind = CXXRecordDecl::LDK_NeverDependent;
CXXRecordDecl *OldClass = E->getLambdaClass();
diff --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp
index 0b7580f91043c7..ef04cad4eef98b 100644
--- a/clang/test/SemaTemplate/concepts-lambda.cpp
+++ b/clang/test/SemaTemplate/concepts-lambda.cpp
@@ -90,6 +90,58 @@ struct Foo {
static_assert(ConstructibleWithN<Foo>);
+namespace GH56556 {
+
+template <typename It>
+inline constexpr It declare ();
+
+template <typename It, template <typename> typename Template>
+concept D = requires {
+ { [] <typename T1> (Template<T1> &) {}(declare<It &>()) };
+};
+
+template <typename T>
+struct B {};
+
+template <typename T>
+struct Adapter;
+
+template <D<B> T>
+struct Adapter<T> {};
+
+template struct Adapter<B<int>>;
+
+} // namespace GH56556
+
+namespace GH82849 {
+
+template <class T>
+concept C = requires(T t) {
+ [](T) {}(t);
+};
+
+template <class From>
+struct Widget;
+
+template <C F>
+struct Widget<F> {
+ static F create(F from) {
+ return from;
+ }
+};
+
+template <class>
+bool foo() {
+ return C<int>;
+}
+
+void bar() {
+ // https://github.com/llvm/llvm-project/issues/49570#issuecomment-1664966972
+ Widget<char>::create(0);
+}
+
+} // namespace GH82849
+
}
// GH60642 reported an assert being hit, make sure we don't assert.
|
clang/lib/Sema/TreeTransform.h
Outdated
if (DC->getDeclKind() == Decl::Kind::RequiresExprBody) | ||
DC = DC->getParent(); |
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.
Do we get into the same issue if there are multiple nested requires
?
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.
Oops, this should be a while
loop, I think
@cor3ntin Thanks! I have updated the test to reflect the nested |
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.
LGTM
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.
Thank you so much! This looks good.
Hi folks, we've started seeing "implicit instantiation of undefined template" compilation errors after this commit. In all cases code used to compile fine before this commit. I haven't spotted any particular issues in the code (though the examples are looked at are rather convoluted, thus, I'm not 100% sure). I'll try to prepare a self-sufficient test case. Was this sort of a behavior change expected? |
I don't think so; a reproducer would be really appreciated. |
I'm reducing the test case. In the meantime, should this be reverted? |
I think this depends on the case. Note the issues addressed by this patch may not only cause some crashes in debug build, but probably lead to incorrect constraint evaluation in release build, which means there's possibility that your case was an accept-invalid before. (Sorry for making confusion earlier; my office hour just begins and I'm sitting here for your case. If there is really something wrong caused by this PR, I'll revert it in time. Don't worry.) |
I have a reduced test case, which is accepted by clang 18, but fails with this patch: https://gcc.godbolt.org/z/zMbKvsf7K |
A bit more compact one: https://gcc.godbolt.org/z/4rzYPKaze |
Can reduce further to:
But, is |
FWIW, @jyknight's example fails to compile with GCC - succeeds with clang 18 release but assert-fails with clang 18 +Asserts build. (not sure about the original/full internal reproduction - we have a way to run compile with assertions enaled, but I'm not sure I'm holding it right... ) |
Your case is rejected by all mainstream compilers as of now: https://gcc.godbolt.org/z/nrMf3zvfY To clarify why the requirement should be evaluated to false:
|
I guess the reduction could have dropped some important parts of this. Let me try the original code with assertions-enabled clang build... |
It turns out debug build of clang before this patch generated an assertion on the original code as well:
I'll try to figure out what's wrong with the original code. |
The internal code was fixed. Thanks everyone for helping to figure this out! |
The dependency of a lambda inside of a
RequiresExprBodyDecl
was previously affected by its parent, e.g.,ClassTemplateSpecializationDecl
. This made the lambda always dependent regardless of the template arguments we had, which caused some crashes on the constraint evaluation later.This fixes #56556, fixes #82849 and a case demonstrated by #49570 (comment).