Skip to content
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] fix crash in codegen stage when an lambda expression declared in an unevaluated context #80802

Closed

Conversation

jcsxky
Copy link
Contributor

@jcsxky jcsxky commented Feb 6, 2024

Try to fix issue
When transform a lambda expression which is declared in an unevaluated context, isInstantiationDependentType() and isVariablyModifiedType() both return false and lead to skip transforming the lambda expression. On the other hand, AlreadyTransformed also skip transformation in this case. Add the condition to check whether it's in decltype makes it work.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Feb 6, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Feb 6, 2024

@llvm/pr-subscribers-clang

Author: Qizhi Hu (jcsxky)

Changes

Try to fix issue
When transform a lambda expression which is declared in an unevaluated context, isInstantiationDependentType() and isVariablyModifiedType() both return false and lead to skip transforming the lambda expression. On the other hand, AlreadyTransformed also skip transform in this case. Add the condition to check whether it's in decltype makes it work.


Full diff: https://github.com/llvm/llvm-project/pull/80802.diff

3 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+2)
  • (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+4-3)
  • (added) clang/test/SemaTemplate/PR76674.cpp (+11)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4d57ea4fd55b8..7ad575e4f57fa 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -204,6 +204,8 @@ Bug Fixes to C++ Support
   parameter where we did an incorrect specialization of the initialization of
   the default parameter.
   Fixes (`#68490 <https://github.com/llvm/llvm-project/issues/68490>`_)
+- Fix a crash in codegen when lambdas declared in an unevaluated context.
+  Fixes (`#76674 <https://github.com/llvm/llvm-project/issues/76674>`_)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 6d59180bc446d..383163ea969a9 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1613,8 +1613,8 @@ namespace {
 bool TemplateInstantiator::AlreadyTransformed(QualType T) {
   if (T.isNull())
     return true;
-
-  if (T->isInstantiationDependentType() || T->isVariablyModifiedType())
+  if (T->isInstantiationDependentType() || T->isVariablyModifiedType() ||
+      (SemaRef.getLangOpts().CPlusPlus20 && T->isDecltypeType()))
     return false;
 
   getSema().MarkDeclarationsReferencedInType(Loc, T);
@@ -2685,7 +2685,8 @@ QualType Sema::SubstType(QualType T,
 
   // If T is not a dependent type or a variably-modified type, there
   // is nothing to do.
-  if (!T->isInstantiationDependentType() && !T->isVariablyModifiedType())
+  if (!T->isInstantiationDependentType() && !T->isVariablyModifiedType() &&
+      (getLangOpts().CPlusPlus20 && !T->isDecltypeType()))
     return T;
 
   TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, Entity);
diff --git a/clang/test/SemaTemplate/PR76674.cpp b/clang/test/SemaTemplate/PR76674.cpp
new file mode 100644
index 0000000000000..50e9053e41e0f
--- /dev/null
+++ b/clang/test/SemaTemplate/PR76674.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -c -std=c++20 -verify=cxx20 -o /dev/null %s
+// expected-no-diagnostics
+
+template <class>
+struct A {
+    template <class U>
+    using Func = decltype([] {return U{};});
+};
+
+A<int>::Func<int> f{};
+int i{f()};

@jcsxky jcsxky force-pushed the fix_lack_transform_crash_of_unevaluated_lambda branch 3 times, most recently from d767344 to 741793c Compare February 6, 2024 06:46

if (T->isInstantiationDependentType() || T->isVariablyModifiedType())
if (T->isInstantiationDependentType() || T->isVariablyModifiedType() ||
(SemaRef.getLangOpts().CPlusPlus20 && T->isDecltypeType()))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add the relevant standards-quote here, particularly for the C++20 vs pre-C++20 change here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this would at best retransform all the decltype which does sound wrong?

I'd rather try to understand why isInstantiationDependentType gives a bogus result, although that might be involved.

Copy link
Contributor Author

@jcsxky jcsxky Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cor3ntin I use this test case locally:

A<double>::Func<long long> f{};

AST looks like:

Breakpoint 42, clang::DecltypeType::DecltypeType (this=0x39d190, E=0x39cfd0, underlyingType=..., can=...) at /home/huqizhi/Downloads/llvm-project/clang/lib/AST/Type.cpp:3797
3797	      E(E), UnderlyingType(underlyingType) {}
(gdb-ChatDBG) p E->dumpColor()
CXXBindTemporaryExpr 0x39cfd0 'class A<double>::(lambda at /home/huqizhi/K.cpp:18:27)' (CXXTemporary 0x39cfd0)
`-LambdaExpr 0x39cfa0 'class A<double>::(lambda at /home/huqizhi/K.cpp:18:27)'
  |-CXXRecordDecl 0x39ca60  implicit class definition
  | |-DefinitionData lambda pass_in_registers empty standard_layout trivially_copyable trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
  | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
  | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
  | | |-MoveConstructor exists simple trivial needs_implicit
  | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
  | | |-MoveAssignment exists simple trivial needs_implicit
  | | `-Destructor simple irrelevant trivial constexpr
  | |-CXXMethodDecl 0x39cbc8  constexpr operator() 'auto (void) const -> auto' implicit_instantiation inline instantiated_from 0x37cc18
  | | `-CompoundStmt 0x39cd58
  | |   `-ReturnStmt 0x39cd48
  | |     `-CXXUnresolvedConstructExpr 0x39cd08 'U' 'U' list
  | |       `-InitListExpr 0x39ccc8 'void'
  | |-CXXConversionDecl 0x39cdf0  implicit constexpr operator auto (*)() 'auto (*(void) const noexcept)(void) -> auto' inline
  | |-CXXMethodDecl 0x39ceb0  implicit __invoke 'auto (void) -> auto' static inline
  | `-CXXDestructorDecl 0x39cff8  implicit referenced constexpr ~(lambda at /home/huqizhi/K.cpp:18:27) 'void (void) noexcept' inline default trivial
  `-CompoundStmt 0x39cd58
    `-ReturnStmt 0x39cd48
      `-CXXUnresolvedConstructExpr 0x39cd08 'U' 'U' list
        `-InitListExpr 0x39ccc8 'void'

CXXBindTemporaryExpr and LambdaExpr both are not InstantiationDependentType

(gdb-ChatDBG) p E->isInstantiationDependent()
$306 = false
(gdb-ChatDBG) p ((CXXBindTemporaryExpr*)E)->getSubExpr()->isInstantiationDependent()
$308 = false

This is because type of the lambda is not InstantiationDependentType:

(gdb-ChatDBG) p ((LambdaExpr*)(((CXXBindTemporaryExpr*)E)->getSubExpr()))->getType().dump()
RecordType 0x39cba0 'class A<double>::(lambda at /home/huqizhi/K.cpp:18:27)'
`-CXXRecord 0x39ca60 ''
$312 = void

I think retransforming all the decltype should be right because retransformation should not change the result. But, this may involve some efficiency problem.
Since CXXUnresolvedConstructExpr in the lambda expression is InstantiationDependentType, Maybe visiting the lambda expression and checking whether it has InstantiationDependentType child is a better approach (I think, but more complex).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add the relevant standards-quote here, particularly for the C++20 vs pre-C++20 change here?

Sorry, I couldn't get what you mean of relevant standards-quote and I am not very familiar with cpp standards. I add SemaRef.getLangOpts().CPlusPlus20 just because command args contains -std=c++20 and clang diagnose says 'lambda expression in an unevaluated operand' and don't retransform decltype all the time(issue only exists in code of cpp20 and later).

@jcsxky jcsxky force-pushed the fix_lack_transform_crash_of_unevaluated_lambda branch 4 times, most recently from 1706840 to 376d5b0 Compare February 8, 2024 11:31
@jcsxky jcsxky force-pushed the fix_lack_transform_crash_of_unevaluated_lambda branch 4 times, most recently from 59fb684 to 0063efb Compare February 11, 2024 07:50
@jcsxky
Copy link
Contributor Author

jcsxky commented Feb 12, 2024

@cor3ntin @erichkeane Could you please take another look at this PR? I think current version would be a reasonable fix since it transforms type only when it's needed. Or you have any other opinions? Thank you for guidance!

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few suggestions, but I REALLY would like @cor3ntin to take a final pass, it mostly LGTM.

@@ -1614,7 +1614,19 @@ bool TemplateInstantiator::AlreadyTransformed(QualType T) {
if (T.isNull())
return true;

if (T->isInstantiationDependentType() || T->isVariablyModifiedType())
bool DependentLambdaType = false;
QualType DesugaredType = T.getDesugaredType(SemaRef.getASTContext());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this step necessary? getAsCXXRecordDecl should at least canonicalize.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, code has been fixed.

bool DependentLambdaType = false;
QualType DesugaredType = T.getDesugaredType(SemaRef.getASTContext());
CXXRecordDecl *RD = DesugaredType->getAsCXXRecordDecl();
if (RD && RD->isLambda()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (auto *RD = T->getAsCXXRecordDecl(); RD && RD->isLambda())

??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@jcsxky jcsxky force-pushed the fix_lack_transform_crash_of_unevaluated_lambda branch 2 times, most recently from 2f09be5 to 5430d07 Compare February 13, 2024 10:26
@jcsxky jcsxky force-pushed the fix_lack_transform_crash_of_unevaluated_lambda branch from 5430d07 to 842f848 Compare February 17, 2024 01:37
@zyn0217
Copy link
Contributor

zyn0217 commented Feb 20, 2024

@jcsxky Do you have more test cases other than the one from #76674? I wonder what'll happen if constraint checking is involved.

@jcsxky
Copy link
Contributor Author

jcsxky commented Feb 21, 2024

@jcsxky Do you have more test cases other than the one from #76674? I wonder what'll happen if constraint checking is involved.

Do you mean concept? If yes, I will have a try.

@cor3ntin
Copy link
Contributor

cor3ntin commented May 3, 2024

This was fixed by #82310

@cor3ntin cor3ntin closed this May 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[clang] Crash when calling object of type obtained from lambda in unevaluated context
6 participants