michaeljclark / crefl

crefl is a runtime library and compiler plug-in to support reflection in C.
38 stars 1 forks source link

Anonymous structure error in function's parameters #3

Open X-Ryl669 opened 2 years ago

X-Ryl669 commented 2 years ago

Take this code for example:

typedef struct {
   int a;
} M;

int foo(const M bar);

Right now, crefl can't reproduce the signature for the foo function because the bar parameter is of type "qualifier(struct)" instead of "qualifier(typedef(struct))". Since the structure is anonymous, the only way to call this function is to use the typedef but this is not what crefl has stored in its database.

So, is there a way to find out the typedef's decl_ref for an anonymous structure's decl_ref ? Or better, would it be possible for crefl to store the parameter as a typedef instead of a struct at first ?

michaeljclark commented 2 years ago

I see the typedef in the metadata. the query interfaces needs usability testing and refinement. this is a point where one was up to.

if you see the code example in the response to https://github.com/michaeljclark/crefl/issues/1, you'll see the use of crefl_type(decl_db*, <type reference>) e.g. crefl_type(db, function f) which returns a _declref based on a stringified type reference. That macro doesn't actually exist yet in the source tree. It probably needs a run-time and compile-time parser for references to types in the crefl decl database. unfortunately, we don't have decltype(T) in C, but also we want to be able to get a reference to type metadata without statically seeing the type i.e. by an unambiguous name-spec.

$ ./scripts/run_tests.py test/input/example-1.h

===== INPUT  test-case: test/input/example-1.h =====

typedef struct {
   int a;
} M;

int foo(const M bar);

===== OUTPUT test-case: test/input/example-1.h =====

sizeof(decl_node)=32
id   attr next link type      name           props          detail              
--------------------------------------------------------------------------------
23   0    0    24   source    example-1.h                   struct("anonymous") 
24   0    26   25   struct    (anonymous)                   field("a")          
25   0    0    10   field     a                             intrinsic("int")    
26   0    27   24   typedef   M                             struct("anonymous") 
27   0    0    28   function  foo            extern_c,globa…param("anonymous")  
28   0    29   10   param     (anonymous)    out            intrinsic("int")    
29   0    0    30   param     bar                           qualifier("const_") 
30   0    0    24   qualifier const_         const          struct("anonymous") 
--------------------------------------------------------------------------------

It might be possible to formulate the references to the types in less ambiguous ways than C. i.e. Rust-style or Go-style where types are left-to-right instead of a complex mix of left-to-right and right-to-left due to C pointer specification mess.

try ./scripts/run_tests.py test/input/pointer-to-array-3.h to see what I mean.

$ ./scripts/run_tests.py --dump-ext test/input/pointer-to-array-3.h 

===== INPUT  test-case: test/input/pointer-to-array-3.h =====

int f(int n, const int(*p)[n][10]);

===== OUTPUT test-case: test/input/pointer-to-array-3.h =====

sizeof(decl_node)=32
id   attr next link type      name                        props                    detail                        
-----------------------------------------------------------------------------------------------------------------
23   0    0    24   source    pointer-to-array-3.h                                 function("f")                 
24   0    0    25   function  f                           extern_c,global,addr=0x0 param("anonymous")            
25   0    26   10   param     (anonymous)                 out                      intrinsic("int")              
26   0    27   10   param     n                                                    intrinsic("int")              
27   0    0    31   param     p                                                    pointer("*[*][10]const_int")  
28   0    0    10   qualifier const_int                   const                    intrinsic("int")              
29   0    0    28   array     [10]const_int               size=10                  qualifier("const_int")        
30   0    0    29   array     [*][10]const_int            vla,size=0               array("[10]const_int")        
31   0    0    30   pointer   *[*][10]const_int           width=64                 array("[*][10]const_int")     
-----------------------------------------------------------------------------------------------------------------
X-Ryl669 commented 2 years ago

Maybe have a look to #5, I've tried to solve this when it isn't ambiguous. It gives something like this:

===== OUTPUT test-case: test/input/example-1.h =====

sizeof(decl_node)=32
id   attr next link type      name           props          detail              
--------------------------------------------------------------------------------
23   0    0    24   source    example-1.h                   struct("anonymous") 
24   0    26   25   struct    (anonymous)                   field("a")          
25   0    0    10   field     a                             intrinsic("int")    
26   0    27   24   typedef   M                             struct("anonymous") 
27   0    0    28   function  foo            extern_c,globa…param("anonymous")  
28   0    29   10   param     (anonymous)    out            intrinsic("int")    
29   0    0    30   param     bar                           qualifier("const_") 
30   0    0    24   qualifier const_         const          typedef(M)
--------------------------------------------------------------------------------
michaeljclark commented 2 years ago

I think I see what you mean from reading the output.

typedefs are desugared so the function links to the anonymous structure decl but do you want a link to the typedef instead?

I had wanted that also but I don't know the implications of a particular approach yet as not all clang AST methods are robust.

the test cases have not been "buttoned down" yet so to speak i.e. we presently use visual inspection of the resulting graphs and intend to create exemplars after inspection to tie the behaviour to the expected output. so the test cases we have at present are inputs that we need to handle but there is no reporting of errors unless. locally I save the output of run_tests.py and diff it.

I will take a look at the pull request...