This is an archive of the discontinued LLVM Phabricator instance.

[clang][ASTImporter] Fix a bug when importing CXXDefaultInitExpr.
ClosedPublic

Authored by balazske on Mar 2 2022, 8:40 AM.

Details

Summary

The "in-class initializer" expression should be set in the field of a
default initialization expression before this expression node is created.
The CXXDefaultInitExpr objects are created after the AST is loaded and
at import not present in the "To" AST. And the in-class initializers of
the used fields can be missing too, these must be set at import.

This fixes a github issue #54061.

Diff Detail

Event Timeline

balazske created this revision.Mar 2 2022, 8:40 AM
Herald added a reviewer: shafik. · View Herald Transcript
Herald added a project: Restricted Project. · View Herald Transcript
balazske requested review of this revision.Mar 2 2022, 8:40 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 2 2022, 8:40 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
balazske updated this revision to Diff 412665.Mar 3 2022, 3:57 AM

Improved the test to fix failure on Windows.

Looks good to me.

martong accepted this revision.Mar 9 2022, 1:29 AM

So, my understanding is that the issue stems from the fact that hasInClassInitializer() gave inconsistent results with getInClassInitializer() for previously imported nodes. Please confirm. For that, the solution you provide seems reasonable and the test cases gives a proper coverage.

clang/lib/AST/ASTImporter.cpp
3649–3650

It is unfortunate to have such an API. Anyway, the assertions you placed seems to address this mess. Good.

clang/unittests/AST/ASTImporterTest.cpp
536–547

So these tests failed with the baseline?

This revision is now accepted and ready to land.Mar 9 2022, 1:29 AM

So, my understanding is that the issue stems from the fact that hasInClassInitializer() gave inconsistent results with getInClassInitializer() for previously imported nodes.

Not really the API is the problem. The real problem was that to create a CXXDefaultInitExpr the field should have a "in-class initializer". CXXDefaultInitExpr has a pointer to the field that is initialized at that place. The field has an "in-class initializer", this is the used expression to initialize the field (CXXDefaultInitExpr is a separate object that is replicated for every initialization in constructors and initializer-list). The in-class initializer expression is not always stored in the AST, in the ToTU it is missing initially. The field has the flag set that it contains in-class initializer but the actual expression is not set yet (probably because the code parser works this way). This expression should be imported before a CXXDefaultInitExpr can be created.

The other code change (at lines 3650-60) is needed because setInClassInitializer can be called only if the value is not set already, otherwise it will assert.

clang/unittests/AST/ASTImporterTest.cpp
536–547

This test was added only because there was no test for CXXDefaultInitExpr. Probably this test does not fail without the fix. The failing test is only the added lit test.

Okay, thanks for the explanation.

The in-class initializer expression is not always stored in the AST, in the ToTU it is missing initially. The field has the flag set that it contains in-class initializer but the actual expression is not set yet (probably because the code parser works this way).

So, the parser can really create such a node when the expression is not set? That seems like a bug in the parser. Anyway, would be nice to see a test case for it and perhaps fix in the parser.
But I have doubts that it would be a parser issue. I wonder whether it is rather this case: first we do an import that fails to set the expression and then the subsequent import crashes?

I think that the problem may be related to the fact that the in-class initializer is not used by the code in the "To" AST (in ctu-cxxdefaultinitexpr.cpp the problem is with QMultiHash::d field). Probably the expression node in the field is set only if it is used by any CXXDefaultInitExpr, but the hasInClassInitializer value is set always if the code contains in-class initializer. I can try to make a unit test for this case (but the lit test is good to have because it is more complex and can reveal other problems).

I think that the problem may be related to the fact that the in-class initializer is not used by the code in the "To" AST (in ctu-cxxdefaultinitexpr.cpp the problem is with QMultiHash::d field). Probably the expression node in the field is set only if it is used by any CXXDefaultInitExpr, but the hasInClassInitializer value is set always if the code contains in-class initializer. I can try to make a unit test for this case ...

Thanks, that'd be great.

... (but the lit test is good to have because it is more complex and can reveal other problems).

I agree.

balazske updated this revision to Diff 414642.Mar 11 2022, 6:05 AM

Added unit tests.

The last unit test is not much different from the lit test, I could not find more simple code to get the wanted result. Is it still good to have the lit test too?

balazske marked 2 inline comments as done.Mar 11 2022, 6:14 AM

Ping
I had a question about if the lit-test is needed with the new unit tests (that contain essentially similar code). But if there is no answer I will commit it with all tests, more test is always better.