[cfe-dev] [ASTImporter] Import order problems

Gábor Horváth via cfe-dev cfe-dev at lists.llvm.org
Thu Sep 7 07:25:21 PDT 2017


Hi!

I stumbled upon a serious issue within the AST Importer. First I will walk
you through a very synthetic example, then I will try to explain what is
happening and finally, I will ask for help if you have a proper solution in
mind.

*The description of the problem*

*1. *Let's have a simple struct (please ignore the UB in the struct) in
a.cpp:
struct A {
  int b = a + 2;
  int a = 5;
};

*2.* Let's also have an empty b.cpp.
*3.* Let's dump the AST of a.cpp:
clang -cc1 -emit-pch -o 1.ast a.cpp -std=c++11
*4.* Let's merge the dumped AST with the empty file and print the AST:
clang -cc1 -ast-merge 1.ast -ast-print b.cpp -std=c++11

The output is:
struct A {
    int a = 5;
    int b = this->a + 2;
};

Note that the order of the fields is changed. This is a serious problem
since the imported type will not be equivalent to the original one and this
can cause various problems during importing non-trivial AST. The same issue
can be triggered without having UB in the code, but I wanted to keep the
example as small as possible.

This importing strategy also has further implications. Such as sometimes
the ASTImporter ends up checking for equivalence of half done and full
definitions. (And they are, of course, not equivalent.)

*Possible explanation of the problem*

During importing a DeclContext we will add each of the declarations to it.
The order of the declarations will be the order in which we add the
declarations. In case a declaration is also a definition, we might end up
importing the definitions as well before finishing the import of the actual
declaration. If a definition refers to a declaration that was not imported
yet, we will start importing that declaration.

In the example above the importer starts with importing A::b. During
importing A::b it will encounter a DeclRefExpr to A::a, which is not
imported yet. The importer will continue with importing A::a. After the
import of A::a is finished, it will be added to the DeclContext before the
import of A::b is finished, thus A::a end up before A::b in the imported
class.

*Possible solutions to the problem*

One solution would be to emulate what the parser does, i.e.: first
importing only the declarations and after all the declarations are imported
continue with the definitions. Unfortunately, I do not see an easy way to
implement this logic with the current structure of the ASTImporter, but
hopefully, it is only because I am not very familiar with that part of
clang.

To circumvent this issue I ended up reordering the declarations within the
DeclContext after finishing the import of the definition based on the
original DeclContext. This feels like a hacky solution for this problem and
also do not solve further implications described above.

*Asking for help*

What do you think? Did you observe similar anomalies? Do you have any
comments or a solution in mind?

Thanks in advance,
Gábor Horváth
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20170907/245d54fd/attachment.html>


More information about the cfe-dev mailing list