-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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][dataflow] Treat comma operator correctly in getResultObjectLocation()
.
#78427
[clang][dataflow] Treat comma operator correctly in getResultObjectLocation()
.
#78427
Conversation
@llvm/pr-subscribers-clang-analysis @llvm/pr-subscribers-clang Author: None (martinboehme) ChangesFull diff: https://github.com/llvm/llvm-project/pull/78427.diff 2 Files Affected:
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index a50ee57a3c11b4..99ed55fee779a3 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -781,8 +781,13 @@ Environment::getResultObjectLocation(const Expr &RecordPRValue) const {
return Val->getLoc();
}
- // Expression nodes that propagate a record prvalue should have exactly one
- // child.
+ if (auto *Op = dyn_cast<BinaryOperator>(&RecordPRValue);
+ Op && Op->isCommaOp()) {
+ return getResultObjectLocation(*Op->getRHS());
+ }
+
+ // All other expression nodes that propagate a record prvalue should have
+ // exactly one child.
llvm::SmallVector<const Stmt *> children(RecordPRValue.child_begin(),
RecordPRValue.child_end());
assert(children.size() == 1);
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 056c4f3383d832..9eabbdfb423a1f 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2642,14 +2642,17 @@ TEST(TransferTest, ResultObjectLocation) {
};
void target() {
- A();
+ 0, A();
(void)0; // [[p]]
}
)";
+ using ast_matchers::binaryOperator;
using ast_matchers::cxxBindTemporaryExpr;
using ast_matchers::cxxTemporaryObjectExpr;
using ast_matchers::exprWithCleanups;
using ast_matchers::has;
+ using ast_matchers::hasOperatorName;
+ using ast_matchers::hasRHS;
using ast_matchers::match;
using ast_matchers::selectFirst;
using ast_matchers::traverse;
@@ -2659,26 +2662,33 @@ TEST(TransferTest, ResultObjectLocation) {
ASTContext &ASTCtx) {
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
- // The expresssion `A()` in the code above produces the following
- // structure, consisting of three prvalues of record type.
+ // The expression `0, A()` in the code above produces the following
+ // structure, consisting of four prvalues of record type.
// `Env.getResultObjectLocation()` should return the same location for
// all of these.
auto MatchResult = match(
traverse(TK_AsIs,
exprWithCleanups(
- has(cxxBindTemporaryExpr(
- has(cxxTemporaryObjectExpr().bind("toe")))
- .bind("bte")))
+ has(binaryOperator(
+ hasOperatorName(","),
+ hasRHS(cxxBindTemporaryExpr(
+ has(cxxTemporaryObjectExpr().bind(
+ "toe")))
+ .bind("bte")))
+ .bind("comma")))
.bind("ewc")),
ASTCtx);
auto *TOE = selectFirst<CXXTemporaryObjectExpr>("toe", MatchResult);
ASSERT_NE(TOE, nullptr);
+ auto *Comma = selectFirst<BinaryOperator>("comma", MatchResult);
+ ASSERT_NE(Comma, nullptr);
auto *EWC = selectFirst<ExprWithCleanups>("ewc", MatchResult);
ASSERT_NE(EWC, nullptr);
auto *BTE = selectFirst<CXXBindTemporaryExpr>("bte", MatchResult);
ASSERT_NE(BTE, nullptr);
RecordStorageLocation &Loc = Env.getResultObjectLocation(*TOE);
+ EXPECT_EQ(&Loc, &Env.getResultObjectLocation(*Comma));
EXPECT_EQ(&Loc, &Env.getResultObjectLocation(*EWC));
EXPECT_EQ(&Loc, &Env.getResultObjectLocation(*BTE));
});
|
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.
This change itself looks good to me, but I start to doubt whether we actually need getResultObjectLocation
the way it is currently implemented. One of the red flags for me seeing some getResultObjectLocation
calls in UncheckedOptionalAccessModel.cpp
. I think most (all?) of those calls are redundant, the type of the expression already guarantees that we have the right location. Moreover, I think the propagation should happen internally in the built-in transfers, so authors of user code like UncheckedOptionalAccessModel
should never need to think about these implementation details.
I think it might make sense to review all of the calls and after we removed the redundant ones, we might want to reconsider if it makes sense to restrict the use of this function somewhat.
Let me know if I am missing something.
The missing context here, I think, is that the current implementation of Let me expand. Currently, But this is wrong. For example, consider the following code: A a = some_condition()? A(1) : A(2, 3); When either the What we should instead be doing is propagating the result object location downwards from the result object to any prvalues that might initialize it. This is described in a FIXME here. (By the way, this is what CodeGen does too -- not surprisingly.) The current implementation is also terrible in other ways. We do actually get the following right: A a = A(1); If you query A nice side effect of fixing Getting back to your original point: Yes, many of the existing This has been pretty lengthy, but I hope it gives more context. |
Yeah, this was really helpful, thanks! This makes a lot of sense to me, I agree that this is a facility that we need. On the other hand, I am still wondering whether it is a good idea to expose this publicly to the checks or we could do it automatically when necessary in the engine code. That being said, I think the "publicness" of this API is not that important to solve at this point. |
You mean the publicness of I don't really see a good alternative. Consider the UncheckedOptionalAccessModel. It needs to provide its own custom transfer function for the constructor of an Maybe you're reacting to the verbose and slightly opaque name? I don't love the name either, but it kind of follows from the terms that the standard uses. "Result object" is the term that the standard uses for "what is the object that this prvalue ends up initializing", and given that term, But we'll certainly keep this on the table -- maybe we come up with a good alternative in the future. |
Yeah, I was wondering if it would be more user friendly if something like |
Thanks, I understand now. I see the attraction, but I think it would dilute the semantics of Extending I think it would therefore be better to make this difference in semantics clear by separating out the "get the result object location for a record prvalue" operation into a separate method. |
No description provided.