-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[clang][AST] Handle dependent representation of call to function with explicit object parameter in CallExpr::getBeginLoc() #126868
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
Conversation
@llvm/pr-subscribers-clang Author: Nathan Ridge (HighCommander4) ChangesFixes #126720 Full diff: https://github.com/llvm/llvm-project/pull/126868.diff 2 Files Affected:
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index c22aa66ba2cfb..55ef3f4748e29 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -1648,8 +1648,11 @@ SourceLocation CallExpr::getBeginLoc() const {
if (const auto *Method =
dyn_cast_if_present<const CXXMethodDecl>(getCalleeDecl());
Method && Method->isExplicitObjectMemberFunction()) {
- assert(getNumArgs() > 0 && getArg(0));
- return getArg(0)->getBeginLoc();
+ if (!isTypeDependent()) {
+ assert(getNumArgs() > 0 && getArg(0));
+ if (getNumArgs() > 0 && getArg(0))
+ return getArg(0)->getBeginLoc();
+ }
}
SourceLocation begin = getCallee()->getBeginLoc();
diff --git a/clang/test/AST/ast-dump-cxx2b-deducing-this.cpp b/clang/test/AST/ast-dump-cxx2b-deducing-this.cpp
index 854d12b4cdba6..abe9d6a5b5bc6 100644
--- a/clang/test/AST/ast-dump-cxx2b-deducing-this.cpp
+++ b/clang/test/AST/ast-dump-cxx2b-deducing-this.cpp
@@ -13,3 +13,16 @@ void main() {
// CHECK-NEXT: | `-DeclRefExpr 0x{{[^ ]*}} <col:13> 'int (S &)' lvalue CXXMethod 0x{{[^ ]*}} 'f' 'int (S &)'
}
}
+
+namespace GH1269720 {
+template <typename T>
+struct S {
+ void f(this S&);
+ void g(S s) {
+ s.f();
+ }
+ // CHECK: CallExpr 0x{{[^ ]*}} <line:22:5, col:9> '<dependent type>'
+ // CHECK-NEXT: `-MemberExpr 0x{{[^ ]*}} <col:5, col:7> '<bound member function type>' .f
+ // CHECK-NEXT: `-DeclRefExpr 0x{{[^ ]*}} <col:5> 'S<T>' lvalue ParmVar 0x{{[^ ]*}} 's' 'S<T>'
+};
+}
|
clang/lib/AST/Expr.cpp
Outdated
@@ -1648,8 +1648,11 @@ SourceLocation CallExpr::getBeginLoc() const { | |||
if (const auto *Method = | |||
dyn_cast_if_present<const CXXMethodDecl>(getCalleeDecl()); | |||
Method && Method->isExplicitObjectMemberFunction()) { | |||
assert(getNumArgs() > 0 && getArg(0)); | |||
return getArg(0)->getBeginLoc(); | |||
if (!isTypeDependent()) { |
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.
My rationale for checking isTypeDependent()
on this line is that I traced the difference between the dependent and non-dependent cases discussed in this comment to this check in Sema::BuildCallExpr()
.
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.
Can we move the isTypeDependent()
check up to the point where we check isExplicitObjectMemberFunction()
, and add a FIXME explaining why this only applies to non-dependent call expressions?
i.e.
// FIXME: Dependent call expressions are not handled because ...
if (!isTypeDependent()) {
...
}
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.
Done.
Note, I added a comment, but not a "FIXME", as there isn't anything remaining to fix -- the fall-through behaviour (returning getCallee()->getBeginLoc()
) is the correct one for the dependent case, because in the dependent case the callee includes the object argument.
clang/lib/AST/Expr.cpp
Outdated
return getArg(0)->getBeginLoc(); | ||
if (!isTypeDependent()) { | ||
assert(getNumArgs() > 0 && getArg(0)); | ||
if (getNumArgs() > 0 && getArg(0)) |
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.
In addition to fixing the reported test case by adding the isTypeDependent()
check, I made this branch more defensive in general, by checking the condition getNumArgs() > 0 && getArg(0)
after asserting it. That way, if there are additional edge cases that would fail this check, they still trip the assertion, but production builds avoid a crash in favour of a much more benign error (a suboptimal begin location).
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.
I don't think we do this elsewhere and in fact I think we prefer a crash we are violating an invariant and we want a hard fail so we can identify and fix ASAP.
Most folks don't run w/ assertions enabled and we could be hiding a latent bug for a long time.
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.
IMO, the assert
by itself is sufficient. Otherwise everyone is paying the overhead for the check which should never fail anyway.
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.
I agree with these arguments against checking an asserted condition in general.
However, I think in this case it might make sense, for two reasons:
- My level of confidence that "((callee is an explicit object member function) and (call is not dependent)) implies (call has a first argument)" is in fact an invariant is not very high.
- The impact of hiding a bug here is fairly low (slightly inaccurate source range, likely to only affect tools like clangd which perform hit-testing on the AST) compared to a production crash.
I'm happy to remove the check if you feel that's the right tradeoff in spite of these considerations, but I wanted to call them out.
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.
Thanks for the patch. While this may not be the most optimal solution, we can likely go with it as-is to put the fire out.
(I think @cor3ntin is not present right now)
clang/lib/AST/Expr.cpp
Outdated
@@ -1648,8 +1648,11 @@ SourceLocation CallExpr::getBeginLoc() const { | |||
if (const auto *Method = | |||
dyn_cast_if_present<const CXXMethodDecl>(getCalleeDecl()); | |||
Method && Method->isExplicitObjectMemberFunction()) { | |||
assert(getNumArgs() > 0 && getArg(0)); | |||
return getArg(0)->getBeginLoc(); | |||
if (!isTypeDependent()) { |
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.
Can we move the isTypeDependent()
check up to the point where we check isExplicitObjectMemberFunction()
, and add a FIXME explaining why this only applies to non-dependent call expressions?
i.e.
// FIXME: Dependent call expressions are not handled because ...
if (!isTypeDependent()) {
...
}
Can we please add more details in the summary about e.g. "This fixes a crash when ... and the fix is ..." |
a1c57ce
to
9430060
Compare
… explicit object parameter in CallExpr::getBeginLoc() This fixes a crash where CallExpr::getBeginLoc() tries to access the first argument of a CallExpr representing a call to a function with an explicit object parameter, assuming that a first argument exists because it's the object argument. This is the case for non-dependent calls, but for dependent calls the object argument is part of the callee (the semantic analysis that separates it out has not been performed yet) and so there may not be a first argument. Fixes #126720
9430060
to
dc03bb6
Compare
Given this comment (i.e. that the behaviour with this patch is correct for the dependent case as well), is there anything else not optimal about this solution? |
Done. |
I was considering finding a better AST model, but it's more complex than the approach so this is good enough for now :P |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/88/builds/8011 Here is the relevant piece of the build log for the reference
|
… explicit object parameter in CallExpr::getBeginLoc() (llvm#126868) This fixes a crash where CallExpr::getBeginLoc() tries to access the first argument of a CallExpr representing a call to a function with an explicit object parameter, assuming that a first argument exists because it's the object argument. This is the case for non-dependent calls, but for dependent calls the object argument is part of the callee (the semantic analysis that separates it out has not been performed yet) and so there may not be a first argument. Fixes llvm#126720
… explicit object parameter in CallExpr::getBeginLoc() (llvm#126868) This fixes a crash where CallExpr::getBeginLoc() tries to access the first argument of a CallExpr representing a call to a function with an explicit object parameter, assuming that a first argument exists because it's the object argument. This is the case for non-dependent calls, but for dependent calls the object argument is part of the callee (the semantic analysis that separates it out has not been performed yet) and so there may not be a first argument. Fixes llvm#126720 (cherry picked from commit 32c8754)
… explicit object parameter in CallExpr::getBeginLoc() (llvm#126868) This fixes a crash where CallExpr::getBeginLoc() tries to access the first argument of a CallExpr representing a call to a function with an explicit object parameter, assuming that a first argument exists because it's the object argument. This is the case for non-dependent calls, but for dependent calls the object argument is part of the callee (the semantic analysis that separates it out has not been performed yet) and so there may not be a first argument. Fixes llvm#126720
This fixes a crash where CallExpr::getBeginLoc() tries to access the
first argument of a CallExpr representing a call to a function with
an explicit object parameter, assuming that a first argument exists
because it's the object argument.
This is the case for non-dependent calls, but for dependent calls
the object argument is part of the callee (the semantic analysis
that separates it out has not been performed yet) and so there may
not be a first argument.
Fixes #126720