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][c++20] Fix code coverage mapping crash with generalized NTTPs #85837

Merged
merged 2 commits into from
May 24, 2024

Conversation

bolshakov-a
Copy link
Contributor

Introduced in #78041, originally reported as #79957 and fixed partially in #80050.

OpaqueValueExpr used with TemplateArgument::StructuralValue has no corresponding source expression.

A test case with subobject-referring NTTP added.

@erichkeane, @cor3ntin, @Endilll

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen labels Mar 19, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Mar 19, 2024

@llvm/pr-subscribers-clang-codegen

Author: Andrey Ali Khan Bolshakov (bolshakov-a)

Changes

Introduced in #78041, originally reported as #79957 and fixed partially in #80050.

OpaqueValueExpr used with TemplateArgument::StructuralValue has no corresponding source expression.

A test case with subobject-referring NTTP added.

@erichkeane, @cor3ntin, @Endilll


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

2 Files Affected:

  • (modified) clang/lib/CodeGen/CoverageMappingGen.cpp (+2-1)
  • (modified) clang/test/CoverageMapping/templates.cpp (+2-1)
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index 71215da362d3d0..3a87f935470673 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -2177,7 +2177,8 @@ struct CounterCoverageMappingBuilder
   }
 
   void VisitOpaqueValueExpr(const OpaqueValueExpr* OVE) {
-    Visit(OVE->getSourceExpr());
+    if (const Expr *SE = OVE->getSourceExpr())
+      Visit(SE);
   }
 };
 
diff --git a/clang/test/CoverageMapping/templates.cpp b/clang/test/CoverageMapping/templates.cpp
index 143e566a33cb85..7e7f2208f1145f 100644
--- a/clang/test/CoverageMapping/templates.cpp
+++ b/clang/test/CoverageMapping/templates.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name templates.cpp %s | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name templates.cpp %s | FileCheck %s
 
 template<typename T>
 void unused(T x) {
@@ -30,5 +30,6 @@ namespace structural_value_crash {
 
   void test() {
     tpl_fn<arr>();
+    tpl_fn<&arr[1]>();
   }
 }

@llvmbot
Copy link
Collaborator

llvmbot commented Mar 19, 2024

@llvm/pr-subscribers-clang

Author: Andrey Ali Khan Bolshakov (bolshakov-a)

Changes

Introduced in #78041, originally reported as #79957 and fixed partially in #80050.

OpaqueValueExpr used with TemplateArgument::StructuralValue has no corresponding source expression.

A test case with subobject-referring NTTP added.

@erichkeane, @cor3ntin, @Endilll


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

2 Files Affected:

  • (modified) clang/lib/CodeGen/CoverageMappingGen.cpp (+2-1)
  • (modified) clang/test/CoverageMapping/templates.cpp (+2-1)
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index 71215da362d3d0..3a87f935470673 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -2177,7 +2177,8 @@ struct CounterCoverageMappingBuilder
   }
 
   void VisitOpaqueValueExpr(const OpaqueValueExpr* OVE) {
-    Visit(OVE->getSourceExpr());
+    if (const Expr *SE = OVE->getSourceExpr())
+      Visit(SE);
   }
 };
 
diff --git a/clang/test/CoverageMapping/templates.cpp b/clang/test/CoverageMapping/templates.cpp
index 143e566a33cb85..7e7f2208f1145f 100644
--- a/clang/test/CoverageMapping/templates.cpp
+++ b/clang/test/CoverageMapping/templates.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name templates.cpp %s | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name templates.cpp %s | FileCheck %s
 
 template<typename T>
 void unused(T x) {
@@ -30,5 +30,6 @@ namespace structural_value_crash {
 
   void test() {
     tpl_fn<arr>();
+    tpl_fn<&arr[1]>();
   }
 }

@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name templates.cpp %s | FileCheck %s
// RUN: %clang_cc1 -std=c++20 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name templates.cpp %s | FileCheck %s
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe, separate launch mode should be added instead of changing existing one? OTOH, it was irrelevant to any specific C++ standard.

Copy link
Collaborator

Choose a reason for hiding this comment

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

If you don't have any reason to expect the shared cases might differently in earlier standard versions, this is fine.

@bolshakov-a bolshakov-a changed the title Fix code coverage mapping crash with generalized NTTPs [clang][c++20] Fix code coverage mapping crash with generalized NTTPs Mar 20, 2024
@@ -2177,7 +2177,8 @@ struct CounterCoverageMappingBuilder
}

void VisitOpaqueValueExpr(const OpaqueValueExpr* OVE) {
Visit(OVE->getSourceExpr());
if (const Expr *SE = OVE->getSourceExpr())
Copy link
Collaborator

Choose a reason for hiding this comment

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

I suspect the correct check here is if (OVE->isUnique())? See https://reviews.llvm.org/D39562 . CC @ahatanak.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not all OpaqueValueExprs having a source expression are marked as "unique". Checking isUnique() instead of getSourceExpr() breaks at least handling of the GNU extension of the ?: operator:

    1|      1|int main() {
    2|      1|    int i = 1;
    3|      1|    return (i ? 1 : 0)
                                  ^0        <-- disappears if isUnique() is used
  ------------------
  |  Branch (3:12): [True: 1, False: 0]
  |  Branch (3:13): [True: 1, False: 0]     <-- disappears if isUnique() is used
  ------------------
    4|      1|        ?: 10;
                         ^0
    5|      1|}

Copy link
Collaborator

Choose a reason for hiding this comment

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

If you have a BinaryConditionalOperator, I'm not sure this does the right thing. If I'm following correctly, you end up visiting the condition twice, which is not accurately reflecting the way this actually executes. The correct way to visit a BinaryConditionalOperator is to visit the "common" expression (BinaryConditionalOperator::getCommon()) first, then don't recurse into the OpaqueValueExpr.

Copy link
Contributor Author

@bolshakov-a bolshakov-a Apr 10, 2024

Choose a reason for hiding this comment

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

If I'm following correctly, you end up visiting the condition twice

No, the "true" branch visitation (propagateCounts) is performed only when it is the ordinary ConditionalOperator (https://github.com/llvm/llvm-project/blob/llvmorg-18.1.3/clang/lib/CodeGen/CoverageMappingGen.cpp#L1949).

Do you think it should be rewritten first? It's just the first case found by me where a non-unique OVE has a source expression which is visited. There may be more cases.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The point of the "unique" flag is precisely to indicate whether a subexpression is naturally part of the tree at that point, or it's referring to an expression which should have been evaluated elsewhere. So not visiting a non-unique expression should be the right default.

I didn't realize the existing AbstractConditionalOperator code already had a workaround for this... we should be able to adjust it to compensate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, thanks! Moreover, I've really found a couple of bugs when some expressions referred by OpaqueValueExpr are handled more than once (#88881 and #88898). Regarding BinaryConditionalOperator, I've decided to refactor it in a separate PR and then to introduce the isUnique() check in this PR, hoping that there will be no more regressions.

@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name templates.cpp %s | FileCheck %s
// RUN: %clang_cc1 -std=c++20 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name templates.cpp %s | FileCheck %s
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you don't have any reason to expect the shared cases might differently in earlier standard versions, this is fine.

@bolshakov-a
Copy link
Contributor Author

@erichkeane, @cor3ntin, @Endilll, @efriedma-quic ping.

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.

This seems fine to me, but please give @efriedma-quic a few days to do a final pass through.

bolshakov-a added a commit to bolshakov-a/llvm-project that referenced this pull request Apr 16, 2024
Only unique `OpaqueValueExpr`s should be handled in the mapping
builder, as discussed in llvm#85837. However, `getCond()` returns
non-unique `OpaqueValueExpr` for `BinaryConditionalOperator` (because
it is also used as the "true" branch expression). Use `getCommon()`
instead so as to bypass the `OpaqueValueExpr`.
efriedma-quic pushed a commit that referenced this pull request Apr 19, 2024
Only unique `OpaqueValueExpr`s should be handled in the mapping builder,
as
[discussed](#85837 (comment))
in #85837. However, `getCond()` returns non-unique `OpaqueValueExpr` for
`BinaryConditionalOperator` (because it is also used as the "true"
branch expression). Use `getCommon()` instead so as to bypass the
`OpaqueValueExpr`.
efriedma-quic pushed a commit that referenced this pull request May 15, 2024
`ArrayInitLoopExpr` AST node has two occurences of its as-written
initializing expression in its subexpressions through a non-unique
`OpaqueValueExpr`. It causes double-visiting of the initializing
expression if not handled explicitly, as discussed in #85837.
Introduced in llvm#78041, originally reported as llvm#79957 and fixed partially
in llvm#80050.

`OpaqueValueExpr` used with `TemplateArgument::StructuralValue` has no
corresponding source expression.

A test case with subobject-referring NTTP added.
@bolshakov-a
Copy link
Contributor Author

@efriedma-quic ping.

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

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

LGTM

(In the future, please leave a note on the pull request when you push an update; as far as
I can tell, GitHub doesn't generate a notification when you force-push to the branch.)

@efriedma-quic efriedma-quic merged commit 6be1a15 into llvm:main May 24, 2024
4 checks passed
@bolshakov-a
Copy link
Contributor Author

Ok, got it. Thanks!

@bolshakov-a bolshakov-a deleted the fix_coverage_nttp_crash branch May 24, 2024 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants