This is an archive of the discontinued LLVM Phabricator instance.

[clang][dataflow] fix failing assert in copyRecord
ClosedPublic

Authored by paulsemel on Jul 20 2023, 7:33 AM.

Details

Summary

When dealing with copy constructor, the compiler can emit an
UncheckedDerivedToBase implicit cast for the CXXConstructorExpr of the
base class. In such case, when trying to copy the src storage location
to its destination, we will fail on the assert checking that location
types are the same.

When copying from derived to base class, it is acceptable to break that
assumption to only copy common fields from the base class.

Note: the provided test crashes the process without the changes made to
copyRecord.

Diff Detail

Event Timeline

paulsemel created this revision.Jul 20 2023, 7:33 AM
Herald added a project: Restricted Project. · View Herald Transcript
Herald added a subscriber: martong. · View Herald Transcript
paulsemel requested review of this revision.Jul 20 2023, 7:33 AM
paulsemel updated this revision to Diff 542517.
mboehme added inline comments.Jul 20 2023, 7:51 AM
clang/lib/Analysis/FlowSensitive/RecordOps.cpp
38

Can you assert(DstFieldLoc != nullptr); (similar to the way this was done for SrcFieldLoc before)?

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
2252–2254

Is this actually needed?

2260

Could we simply call B Target and omit its default constructor so that we can avoid all of the custom setup code?

2260

Alternatively, and even better, could we add checks that test that the copy constructor of A is indeed creating a "slice" of the B object?

2272–2275

I don't see a base-class initializer in the code, and this doesn't appear to be what the test is for. Is this copy-pasted from another test?

paulsemel updated this revision to Diff 542805.Jul 21 2023, 1:58 AM
paulsemel marked 3 inline comments as done.
paulsemel set the repository for this revision to rC Clang.
paulsemel added a project: Restricted Project.
paulsemel added inline comments.
clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
2252–2254

Yes, I think so, it's the only way I could find to have the compiler generate the UncheckedDerivedToBase implicit cast.

2260

Not sure what you meant here, but I can't write a body for the copy constructor for B, or otherwise it would create a DerivedToBase implicit cast and not a UncheckedDerivedToBase one anymore... so I essentially can't add an annotation there.

sammccall added inline comments.
clang/lib/Analysis/FlowSensitive/RecordOps.cpp
26

this variable is unused in a non-debug build, can you add (void)compatibleTypes; to suppress the warning?

(Thanks for this patch, have been testing it locally as it fixes crashes we're hitting in nullability)

paulsemel updated this revision to Diff 543520.Jul 24 2023, 6:31 AM

apply Sam's suggested changes.

paulsemel marked an inline comment as done.Jul 24 2023, 6:32 AM

When I originally reviewed this patch, I took only a very cursory look, as I was about to finish up for the day and wanted to get a few initial thoughts out (sorry...).

Having taken a closer look, I think the test for this would better live in RecordOpsTest.cpp. Also, it should just call copyRecord() directly, rather than testing it indirectly through the effects of a copy constructor (as examples, look at some of the other tests there).

So the test could look something like this:

struct A {
  int i;
};

struct B : public A {
};

void target(A a, B b) {
  (void)a.i;
  // [[p]]
}

And then in the callback, you would do something like this:

Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();

const ValueDecl *IDecl = findValueDecl(ASTCtx, "i");
auto &A = getLocForDecl<AggregateStorageLocation>(ASTCtx, Env, "a");
auto &B = getLocForDecl<AggregateStorageLocation>(ASTCtx, Env, "b");

EXPECT_NE(Env.getValue(A.getChild(*IDecl)), Env.getValue(B.getChild(*IDecl)));

copyRecord(A, B, Env);

EXPECT_EQ(Env.getValue(A.getChild(*IDecl)), Env.getValue(B.getChild(*IDecl)));

(There's probably something missing here, but I hope you can fill in the blanks.)

This is closer in spirit to a unit test, and it requires less setup.

Does this make sense?

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
2252–2254

Can you clarify why it's important that the AST specifically contains an UncheckedDerivedToBase cast? (IOW, why do we otherwise not call copyRecord() in a way that tests the case that we're interested in?)

But see also my more general comments on testing.

2260

I see what you mean. So maybe it's not the wisest choice to call the derived class target.

Instead, couldn't we do something like this?

struct A {
  A(const A&) = default;
  int i;
};

struct B : public A {
};

void target() {
  B b = { 1 };
  A a = b;
  // [[p]]
}

And then write checks to verify that in the environment for p, the value for a.i is equal to b.i?

But I think we can do even better (see more general comment with regard to testing that I'm making as a patch-level comment.)

Oh, and also wanted to give you a heads up that the changes in https://reviews.llvm.org/rG44f98d0101fe82352e7c5fa98f1b2e9dc1159200 may conflict with what you have here (should be easy to fix though).

paulsemel updated this revision to Diff 543981.Jul 25 2023, 7:59 AM

I used Martin's test suggestion as-is, since I think it pretty much tests the initial failing reproduction case.

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
2252–2254

Note: this will be outdated since I changed the test to the proposed unittest.

If we're hitting a DerivedToBase, we'll just not go there because the latter is not handled in VisitImplicitCastExpr, which is why it was first hard for me to reproduce that failing assert.

paulsemel updated this revision to Diff 543983.Jul 25 2023, 8:01 AM

rename test.

mboehme added inline comments.Jul 25 2023, 11:56 PM
clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
2252–2254

Ah, got it, thanks for the explanation!

mboehme accepted this revision.Jul 26 2023, 12:01 AM

Thanks, this is great!

This revision is now accepted and ready to land.Jul 26 2023, 12:01 AM
This revision was landed with ongoing or failed builds.Jul 26 2023, 1:53 AM
This revision was automatically updated to reflect the committed changes.