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] Fix dependency computation for pack indexing expression #91933

Merged
merged 6 commits into from
May 14, 2024

Conversation

cor3ntin
Copy link
Contributor

Given foo...[idx] if idx is value dependent, the expression is type dependent.

Fixes #91885
Fixes #91884

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

llvmbot commented May 13, 2024

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

Changes

Given foo...[idx] if idx is value dependent, the expression is type dependent.

Fixes #91885
Fixes #91884


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

2 Files Affected:

  • (modified) clang/lib/AST/ComputeDependence.cpp (+3)
  • (modified) clang/test/SemaCXX/cxx2c-pack-indexing.cpp (+14)
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index bad8e75b2f878..ee56c50d76512 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -376,6 +376,9 @@ ExprDependence clang::computeDependence(PackExpansionExpr *E) {
 
 ExprDependence clang::computeDependence(PackIndexingExpr *E) {
   ExprDependence D = E->getIndexExpr()->getDependence();
+  if (D & ExprDependence::Value)
+    D |= ExprDependence::TypeInstantiation;
+
   ArrayRef<Expr *> Exprs = E->getExpressions();
   if (Exprs.empty())
     D |= (E->getPackIdExpression()->getDependence() |
diff --git a/clang/test/SemaCXX/cxx2c-pack-indexing.cpp b/clang/test/SemaCXX/cxx2c-pack-indexing.cpp
index a3e5a0931491b..764f6163710bd 100644
--- a/clang/test/SemaCXX/cxx2c-pack-indexing.cpp
+++ b/clang/test/SemaCXX/cxx2c-pack-indexing.cpp
@@ -194,3 +194,17 @@ void h() {
   // expected-note-re@-2 {{function template specialization '{{.*}}' requested here}}
 }
 }
+
+namespace GH91885 {
+
+void test(auto...args){
+    [&]<int idx>(){
+        using R = decltype( args...[idx] ) ;
+    }.template operator()<0>();
+}
+
+void f( ) {
+    test(1);
+}
+
+}

Given `foo...[idx]` if idx is value dependent, the expression
is type dependent.

Fixes llvm#91885
Fixes llvm#91884
@@ -376,6 +376,9 @@ ExprDependence clang::computeDependence(PackExpansionExpr *E) {

ExprDependence clang::computeDependence(PackIndexingExpr *E) {
ExprDependence D = E->getIndexExpr()->getDependence();
if (D & ExprDependence::Value)
D |= ExprDependence::TypeInstantiation;
Copy link
Contributor

Choose a reason for hiding this comment

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

Does it mean "if the index expression is value-dependent, then the whole PackIndexingExpr is type-dependent despite its expressions"? For example,

template <int N, int... E>
auto foo() {
  return E...[N];
}

Should E...[N] be treated as type-dependent regardless of the result being always an int?

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably E->getPackIdExpression()->getDependence() & ExprDependence::TypeInstantiation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should E...[N] be treated as type-dependent regardless of the result being always an int?

Yes. (We don't do anything to look at whether all the expressions would have the same type)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

NVM, you are right, we can simplify this whole thing, I'll do that shortly

@@ -377,7 +377,7 @@ ExprDependence clang::computeDependence(PackExpansionExpr *E) {
ExprDependence clang::computeDependence(PackIndexingExpr *E) {
ExprDependence D = E->getIndexExpr()->getDependence();
if (D & ExprDependence::Value)
D |= ExprDependence::TypeInstantiation;
D |= E->getPackIdExpression()->getDependence() & ExprDependence::Type;
Copy link

Choose a reason for hiding this comment

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

The change seems reasonable to me, however with it we need to alter the way PackIndexingExpr is handled inside the getDecltypeForExpr, since when 1) E is not type-dependent (int... case) and 2) index is instantiation-dependent we shall not directly call getSelectedExpr.

QualType Sema::getDecltypeForExpr(Expr *E) {
if (E->isTypeDependent())
return Context.DependentTy;
Expr *IDExpr = E;
if (auto *ImplCastExpr = dyn_cast<ImplicitCastExpr>(E))
IDExpr = ImplCastExpr->getSubExpr();
if (auto *PackExpr = dyn_cast<PackIndexingExpr>(E))
IDExpr = PackExpr->getSelectedExpr();

Expr *getSelectedExpr() const {
std::optional<unsigned> Index = getSelectedIndex();
assert(Index && "extracting the indexed expression of a dependant pack");
return getTrailingObjects<Expr *>()[*Index];
}

In short, crash on the following code:

template<int...args> // notice the "int..." here.
void test(){
  [&]<int idx>(){
    using R = decltype( args...[idx] ) ;
  }.template operator()<0>();
}

void f( ) {
  test<1>();
}

Copy link

Choose a reason for hiding this comment

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

Possibly PackExpr->getExpressions().front() for the type-independent PackExpr with its IndexExpr instantiation-dependent? I'm not sure whether we need an empty-check here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The change seems reasonable to me, however with it we need to alter the way PackIndexingExpr is handled inside the getDecltypeForExpr, since when 1) E is not type-dependent (int... case) and 2) index is instantiation-dependent we shall not directly call getSelectedExpr.

In that case (no selected expression) the pack is always type dependent. I did add a test though

Copy link

Choose a reason for hiding this comment

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

In that case (no selected expression) the pack is always type dependent.

I don't get it, could you explain the reason? FWIW, new test failed in CI so we may need to reconsider this.

@@ -9806,7 +9806,7 @@ QualType Sema::BuildCountAttributedArrayType(QualType WrappedTy,
/// that expression, according to the rules in C++11
/// [dcl.type.simple]p4 and C++11 [expr.lambda.prim]p18.
QualType Sema::getDecltypeForExpr(Expr *E) {
if (E->isTypeDependent())
if (E->isInstantiationDependent())
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it... right? I was assuming this would bring such regressions e.g.

template <class T>
const int val = sizeof(T);

template <class T>
void foo() {
  decltype(val<T>);  // <-- this is instantiation-dependent, but not type-dependent.
}

Perhaps we can move it to line 9816 where the PackIndexingExpr is handled?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Perhaps we can move it to line 9816 where the PackIndexingExpr is handled?

I would be slightly concerned about having specific handling of PackIndexingExpr all over the place,
or about decltype behaving differently for pack indexing.

Then again, I'm not sure there is a scenario where it matters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So here is a thought.
If the pack indexing expression is not type dependent, decltype should just produce the type of that expression.
By that logic we do want to modify decltype so that it does something more clever than to try to use the selected expression.

I'll try to do something in that direction, but it's unclear to me under which set of circumstances we can observe that.

If a pack indexing expression has no selected expression
but has a non-tytpe dependent pattern, we can use that type to
compute the decltyoe type.
Copy link
Contributor

@zyn0217 zyn0217 left a comment

Choose a reason for hiding this comment

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

Thanks for the prompt fix! I think this looks good now, but please wait for @erichkeane for the final stamp.

@cor3ntin cor3ntin merged commit 312f83f into llvm:main May 14, 2024
3 of 4 checks passed
@cor3ntin cor3ntin deleted the fix_pack_indexing2 branch May 14, 2024 13:37
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
5 participants