Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are canonical types in Clang?

Tags:

I have a simple header parser based on clang and I get the typedefs from some source.

struct _poire { int g; tomate rouge; }; typedef struct _poire kudamono; 

After parsing this I have a clang::TypedefDecl then I get the clang::QualType of the typedef with clang::TypedefDecl::getUnderlyingType()

With the QualType if I use the getAsString method I can find the "struct _poire" std::string. All this is Ok.

The problem is if I try to see if this type is a canonical type, with QualType::isCanonical(), it returns false.

So I try to get the canonical type with QualType::getCanonicalType().getAsString() and it returns the same string "struct _poire".

according to the clang reference on type http://clang.llvm.org/docs/InternalsManual.html#canonical-types , I thought that the isCanonical() should return true when no typedef is involved.

So what are really canonical type?

like image 587
cedlemo Avatar asked Aug 10 '14 17:08

cedlemo


People also ask

What is LibClang?

LibClang is a stable high level C interface to clang. When in doubt LibClang is probably the interface you want to use. Consider the other interfaces only when you have a good reason not to use LibClang. Canonical examples of when to use LibClang: Xcode.

How do you use clang?

To configure a Visual Studio project to use Clang, right-click on the project node in Solution Explorer and choose Properties. Typically, you should first choose All configurations at the top of the dialog. Then, under General > Platform Toolset, choose LLVM (clang-cl) and then OK.


2 Answers

After further investigations and a question in the clang mailing list, I think I have figured out what is a canonical type.

Firstly it 's important to not focus on the QualType in order to understand Canonical Type. Look this (code /pseudocode):

source file :

typedef struct _poire kudamono;  

clang code :

QualType t = clang::TypedefDecl::getUnderlyingType()  t.getAsString() // "struct _poire" t.isCanonical() // false t.getTypePtr()->getTypeClassName() // ElaboredType  c = t.getCanonicalType() c.getAsString() // "struct _poire" c.isCanonical() // true c.getTypePtr()->getTypeClassName() // RecordType 

c and t are not the same QualType even if they have the same string representation. QualType are used to associate qualifiers ("const", "volatile"...) with a clang type. There are a lot of Clang Types classes because clang needs to keep tracks of the user-specified types for diagnostics.( http://clang.llvm.org/docs/InternalsManual.html#the-type-class-and-its-subclasses and http://clang.llvm.org/doxygen/classclang_1_1Type.html )

The clang types used depends heavily on the syntaxic sugars or modifiers associated with the C/C++ types in the source file.

In the exemple above, the QualType t is associated with an ElaboratedType. This type allows to keep track of the type name as written in the source code. But the canonical QualType is associated with a RecordType.

Another example: source file:

typedef struct _poire kudamono; typedef kudamono tabemono; 

clang code :

QualType t = clang::TypedefDecl::getUnderlyingType() t.getAsString() // "kudamono" t.isCanonical() // false t.getTypePtr()->getTypeClassName() // TypedefType  c = t.getCanonicalType() c.getAsString() // "struct _poire" c.isCanonical() // true c.getTypePtr()->getTypeClassName() // RecordType 

Here we can see that the underlying type of the typedef is recorded as "kudamono" a TypedefType and not "struct _poire" an ElaboratedType.

The canonical type for the TypedefType "kudamono" is a RecordType "struct _poire".

Another examples that I have had from the clang mailing-list ( http://article.gmane.org/gmane.comp.compilers.clang.devel/38371/match=canonical+type ):

Consider:

int (x); 

The type of x is not a BuiltinType; it's a ParenType whose canonical type is a BuiltinType. And given

struct X { int n; }; struct X x; 

the type of x will probably be represented as an ElaboratedType whose canonical type is a RecordType.

So the canonical Type in clang are classes of types that are not associated with any syntaxic sugars or modifiers or typedef (like BuiltinType or RecordType). Other classes of types (like ParentType, TypedefType or ElaboratedType) are used to keep tracks of the user type for diagnostics (error message ...).

like image 118
cedlemo Avatar answered Nov 08 '22 16:11

cedlemo


It seems that you have raised an interesting point. I have figured out something, but since I can't actually test my intuition right now, I can't be 100% sure. Anyway here is what I would do :

If I parse your code (with a little extension to declare a kudamono variable), here is what I can say from this:

struct _poire {     int g;     char rouge; // tomate is probably one of your classes so I just changed the type of the field. }; typedef struct _poire kudamono;  int maFonction(){     kudamono une_poire;     return 0; } 

When the typedef is parsed, here is what is yielded :

-TypedefDecl 0x23b4620 <line:5:1, col:23> kudamono 'struct _poire':'struct _poire'

When I declare a variable of type kudamono, here is below its AST-dump :

-VarDecl 0x2048040 <col:2, col:11> une_poire 'kudamono':'struct _poire'

NB : You can get the AST Dump of your code with this command line, it can be really handy to understand how your code will be parsed :

clang -Xclang -ast-dump -std=c++11 -fsyntax-only test.cpp (just remove -std=c++11 if you want to compile a file_name.c file)

Now, from what I understand, I will make a comparaison between the VarDecl and the TypedefDecl :

1°) This VarDecl is named une_poire and has the type kudamono which is a typedef from the type struct _poire.

2°) This TypedefDecl is named kudamono and has the type struct _poire which is a typedef from the type struct _poire

So, the weird part is right here. struct _poire is considered as typedef from struct _poire.

You'll note that I tried to make a typedef with a usual type :

typedef int numbers;

And this time, AST-dump yields :

TypedefDecl 0x25d9680 <line:7:1, col:13> numbers 'int', so I guess the parser may have some troubles with handmade types (typically structs).

I can see one dirty way to know if your type is canonical or not (without getting false positives or false negatives) :

Check that the QualType and the canonical QualType are not the same

I don't know if a simple '=' between Qualtype will make false positives or false negatives (as I can't test), but you can still compare the names of the types with strcmp

So, to sum up a little bit :

  • Your understanding of a canonical type is fine.
  • Clang seems to have some trouble with handmade types, but it should be fine with Typedef from usual types (such as typedef int int32_t).
  • When you want to know if a type is canonical or not, you can compare the name of the type and the name of the canonical type, but it's quite dirty. On usual type, isCanonical() works well.
like image 38
Marc-O Avatar answered Nov 08 '22 16:11

Marc-O