FreeBASIC  0.91.0
ast-helper.bas
Go to the documentation of this file.
1 '' AST misc helpers/builders
2 ''
3 '' chng: sep/2006 written [v1ctor]
4 
5 
6 #include once "fb.bi"
7 #include once "fbint.bi"
8 #include once "ir.bi"
9 #include once "ast.bi"
10 #include once "lex.bi"
11 #include once "rtl.bi"
12 
13 ''
14 '' vars
15 ''
16 
17 function astBuildVarAssign _
18  ( _
19  byval lhs as FBSYMBOL ptr, _
20  byval rhs as integer _
21  ) as ASTNODE ptr
22 
23  function = astNewASSIGN( astNewVAR( lhs ), astNewCONSTi( rhs ) )
24 
25 end function
26 
27 function astBuildVarAssign _
28  ( _
29  byval lhs as FBSYMBOL ptr, _
30  byval rhs as ASTNODE ptr _
31  ) as ASTNODE ptr
32 
33  function = astNewASSIGN( astNewVAR( lhs ), rhs )
34 
35 end function
36 
37 function astBuildFakeWstringAccess( byval sym as FBSYMBOL ptr ) as ASTNODE ptr
38  assert( symbGetIsWstring( sym ) )
39  function = astNewDEREF( astNewVAR( sym ) )
40 end function
41 
43  ( _
44  byval sym as FBSYMBOL ptr, _
45  byval expr as ASTNODE ptr, _
46  byval options as integer _
47  ) as ASTNODE ptr
48 
49  dim as ASTNODE ptr t = any
50 
51  assert( symbGetIsWstring( sym ) )
52  t = NULL
53 
54  '' side-effect?
55  if( astIsClassOnTree( AST_NODECLASS_CALL, expr ) <> NULL ) then
56  t = astNewLINK( t, astRemSideFx( expr ), FALSE )
57  end if
58 
59  assert( astGetDataType( expr ) = FB_DATATYPE_WCHAR )
60 
61  '' wcharptr = WstrAlloc( WstrLen( expr ) )
62  t = astNewLINK( t, _
63  astBuildVarAssign( sym, rtlWstrAlloc( rtlWstrLen( astCloneTree( expr ) ) ) ), _
64  FALSE )
65 
66  '' *wcharptr = expr
67  t = astNewLINK( t, _
68  astNewASSIGN( astBuildFakeWstringAccess( sym ), expr, options ), _
69  FALSE )
70 
71  function = t
72 end function
73 
74 '':::::
75 function astBuildVarInc _
76  ( _
77  byval lhs as FBSYMBOL ptr, _
78  byval rhs as integer _
79  ) as ASTNODE ptr
80 
81  dim as AST_OPOPT options = any
82  dim as AST_OP op = any
83 
84  options = AST_OPOPT_DEFAULT
85  if( typeIsPtr( symbGetType( lhs ) ) ) then
86  options or= AST_OPOPT_LPTRARITH
87  end if
88 
89  if( rhs > 0 ) then
90  op = AST_OP_ADD_SELF
91  else
92  op = AST_OP_SUB_SELF
93  rhs = -rhs
94  end if
95 
96  function = astNewSelfBOP( op, astNewVAR( lhs ), _
97  astNewCONSTi( rhs ), NULL, options )
98 
99 end function
100 
101 function astBuildVarDeref( byval sym as FBSYMBOL ptr ) as ASTNODE ptr
102  function = astNewDEREF( astNewVAR( sym ) )
103 end function
104 
105 function astBuildVarAddrof( byval sym as FBSYMBOL ptr ) as ASTNODE ptr
106  function = astNewADDROF( astNewVAR( sym ) )
107 end function
108 
109 '':::::
110 function astBuildVarDtorCall _
111  ( _
112  byval s as FBSYMBOL ptr, _
113  byval check_access as integer _
114  ) as ASTNODE ptr
115 
116  '' assuming conditions were checked already
117  function = NULL
118 
119  '' array? dims can be -1 with "DIM foo()" arrays..
120  if( symbGetArrayDimensions( s ) <> 0 ) then
121  '' destruct and/or free array, if needed
122  function = rtlArrayErase( astNewVAR( s ), symbIsDynamic( s ), check_access )
123  else
124  select case symbGetType( s )
125  '' dyn string?
126  case FB_DATATYPE_STRING
127  function = rtlStrDelete( astNewVAR( s ) )
128 
129  '' wchar ptr marked as "dynamic wstring"?
130  case typeAddrOf( FB_DATATYPE_WCHAR )
131  assert( symbGetIsWstring( s ) ) '' This check should be done in symbGetVarHasDtor() already
132  '' It points to a dynamically allocated wchar buffer
133  '' that must be deallocated.
134  function = rtlStrDelete( astNewVAR( s ) )
135 
136  case else
137  '' UDT var with dtor?
138  if( symbHasDtor( s ) ) then
139  if( check_access ) then
140  if( symbCheckAccess( symbGetCompDtor( symbGetSubtype( s ) ) ) = FALSE ) then
141  errReport( FB_ERRMSG_NOACCESSTODTOR )
142  end if
143  end if
144  function = astBuildDtorCall( symbGetSubtype( s ), astNewVAR( s ) )
145  end if
146  end select
147  end if
148 
149 end function
150 
151 function astBuildVarField _
152  ( _
153  byval sym as FBSYMBOL ptr, _
154  byval fld as FBSYMBOL ptr, _
155  byval ofs as longint _
156  ) as ASTNODE ptr
157 
158  dim as ASTNODE ptr expr = any
159 
160  if( fld ) then
161  ofs += symbGetOfs( fld )
162 
163  '' byref or import?
164  if( symbIsParamByRef( sym ) or symbIsImport( sym ) ) then
165  expr = astNewDEREF( _
166  astNewVAR( sym, , typeAddrOf( symbGetFullType( sym ) ), _
167  symbGetSubtype( sym ) ), _
168  symbGetFullType( fld ), symbGetSubtype( fld ), ofs )
169  else
170  expr = astNewVAR( sym, ofs, symbGetFullType( fld ), symbGetSubtype( fld ) )
171  end if
172 
173  expr = astNewFIELD( expr, fld )
174  else
175  '' byref or import?
176  if( symbIsParamByRef( sym ) or symbIsImport( sym ) ) then
177  expr = astNewDEREF( _
178  astNewVAR( sym, , typeAddrOf( symbGetFullType( sym ) ), _
179  symbGetSubtype( sym ) ), _
180  , , ofs )
181  else
182  expr = astNewVAR( sym, ofs )
183  end if
184  end if
185 
186  function = expr
187 end function
188 
189 function astBuildTempVarClear( byval sym as FBSYMBOL ptr ) as ASTNODE ptr
190  '' Don't need to clear if it's a STATIC, it will be initialized on
191  '' startup, and e.g. we should definitely not overwrite a string var
192  '' that was already initialized/used (which could happen with a STATIC),
193  '' because then we'd leak the string memory if any was allocated.
194  if( symbIsStatic( sym ) ) then
195  return NULL
196  end if
197 
198  assert( symbIsShared( sym ) = FALSE )
199  assert( symbIsTemp( sym ) )
200 
201  '' Clear variable's memory
202  function = astNewMEM( AST_OP_MEMCLEAR, astNewVAR( sym ), _
203  astNewCONSTi( symbGetLen( sym ) ) )
204 end function
205 
206 ''
207 '' loops
208 ''
209 
210 function astBuildForBegin _
211  ( _
212  byval tree as ASTNODE ptr, _
213  byval cnt as FBSYMBOL ptr, _
214  byval label as FBSYMBOL ptr, _
215  byval inivalue as integer, _
216  byval flush_label as integer _
217  ) as ASTNODE ptr
218 
219  '' cnt = 0
220  tree = astNewLINK( tree, astBuildVarAssign( cnt, inivalue ) )
221 
222  '' do
223  tree = astNewLINK( tree, astNewLABEL( label, flush_label ) )
224 
225  function = tree
226 end function
227 
228 function astBuildForEnd _
229  ( _
230  byval tree as ASTNODE ptr, _
231  byval cnt as FBSYMBOL ptr, _
232  byval label as FBSYMBOL ptr, _
233  byval endvalue as ASTNODE ptr _
234  ) as ASTNODE ptr
235 
236  '' counter += stepvalue
237  tree = astNewLINK( tree, astBuildVarInc( cnt, 1 ) )
238 
239  '' if( counter = endvalue ) then
240  '' goto label
241  '' end if
242  tree = astNewLINK( tree, _
243  astBuildBranch( _
244  astNewBOP( AST_OP_EQ, astNewVAR( cnt ), endvalue ), _
245  label, FALSE ) )
246 
247  function = tree
248 end function
249 
250 ''
251 '' calls
252 ''
253 
254 function astBuildVtableLookup _
255  ( _
256  byval proc as FBSYMBOL ptr, _
257  byval thisexpr as ASTNODE ptr _
258  ) as ASTNODE ptr
259 
260  dim as ASTNODE ptr p = any
261  dim as integer vtableindex = any
262 
263  if( symbIsVirtual( proc ) ) then
264  vtableindex = symbProcGetVtableIndex( proc )
265  assert( vtableindex > 0 )
266 
267  '' calling virtual method
268  '' method( this )
269  '' becomes
270  '' (*(this.vptr[vtableindex]))( this )
271  '' i.e. the procptr must be read out from the vtable based on
272  '' the vtable index of this method, and then it is called.
273  ''
274  '' The this.vptr points to the 3rd element of the vtable,
275  '' but the vtable index actually is absolute, not relative to
276  '' the 3rd element, so it actually should be:
277  '' (*(this.vptr[vtableindex-2]))( this )
278  ''
279  '' Also, the vptr always is at the top of the object,
280  '' so we can just do:
281  '' (*((*cptr( any ptr ptr ptr, @this ))[vtableindex-2]))( this )
282 
283  '' Get the vtable pointer of type ANY PTR PTR
284  p = astCloneTree( thisexpr )
285  p = astNewADDROF( p )
286  p = astNewCONV( typeMultAddrOf( FB_DATATYPE_VOID, 3 ), NULL, p, AST_CONVOPT_DONTCHKPTR )
287  p = astNewDEREF( p )
288 
289  '' Apply the index
290  p = astNewBOP( AST_OP_ADD, p, astNewCONSTi( vtableindex - 2 ), _
291  NULL, AST_OPOPT_DEFAULT or AST_OPOPT_DOPTRARITH )
292 
293  '' Deref to get the procptr stored in that vtable slot
294  p = astNewDEREF( p )
295 
296  '' Cast to proper procptr type
297  '' (this is important for C/LLVM backends, which are pretty strict about types)
298  p = astNewCONV( typeAddrOf( FB_DATATYPE_FUNCTION ), symbAddProcPtrFromFunction( proc ), p )
299 
300  '' null pointer checking for ABSTRACTs
301  '' (in case it wasn't overridden)
302  if( env.clopt.extraerrchk ) then
303  if( symbIsAbstract( proc ) ) then
304  p = astBuildPTRCHK( p )
305  end if
306  end if
307  else
308  '' Calling normal non-virtual method, nothing to do
309  p = NULL
310  end if
311 
312  function = p
313 end function
314 
315 function astBuildCall _
316  ( _
317  byval proc as FBSYMBOL ptr, _
318  byval arg1 as ASTNODE ptr, _
319  byval arg2 as ASTNODE ptr, _
320  byval arg3 as ASTNODE ptr _
321  ) as ASTNODE ptr
322 
323  dim as ASTNODE ptr p = any, ptrexpr = any
324 
325  '' astBuildCall() is used to call operator overloads - they can be
326  '' virtual methods, at least for self-ops.
327  if( symbIsVirtual( proc ) ) then
328  '' The first arg should be the THIS ptr
329  assert( symbIsMethod( proc ) )
330  assert( astGetDataType( arg1 ) = FB_DATATYPE_STRUCT )
331  assert( astGetSubtype( arg1 ) = symbGetNamespace( proc ) )
332 
333  ptrexpr = astBuildVtableLookup( proc, arg1 )
334  else
335  ptrexpr = NULL
336  end if
337 
338  p = astNewCALL( proc, ptrexpr )
339 
340  if( arg1 ) then
341  if( astNewARG( p, arg1 ) = NULL ) then
342  return NULL
343  end if
344  end if
345 
346  if( arg2 ) then
347  if( astNewARG( p, arg2 ) = NULL ) then
348  return NULL
349  end if
350  end if
351 
352  if( arg3 ) then
353  if( astNewARG( p, arg3 ) = NULL ) then
354  return NULL
355  end if
356  end if
357 
358  '' Take care of functions returning BYREF
359  p = astBuildByrefResultDeref( p )
360 
361  function = p
362 end function
363 
364 function astBuildCtorCall _
365  ( _
366  byval sym as FBSYMBOL ptr, _
367  byval thisexpr as ASTNODE ptr _
368  ) as ASTNODE ptr
369 
370  dim as FBSYMBOL ptr ctor = any
371  dim as ASTNODE ptr proc = any
372  dim as integer params = any
373 
374  ctor = symbGetCompDefCtor( sym )
375  if( ctor = NULL ) then
376  return NULL
377  end if
378 
379  proc = astNewCALL( ctor )
380 
381  astNewARG( proc, thisexpr )
382 
383  '' add the optional params, if any
384  params = symbGetProcParams( ctor ) - 1
385  do while( params > 0 )
386  astNewARG( proc, NULL )
387  params -= 1
388  loop
389 
390  function = proc
391 
392 end function
393 
394 function astBuildDtorCall _
395  ( _
396  byval sym as FBSYMBOL ptr, _
397  byval thisexpr as ASTNODE ptr, _
398  byval ignore_virtual as integer _
399  ) as ASTNODE ptr
400 
401  dim as FBSYMBOL ptr dtor = any
402  dim as ASTNODE ptr callexpr = any
403 
404  '' Can be virtual
405  dtor = symbGetCompDtor( sym )
406  if( ignore_virtual ) then
407  callexpr = astNewCALL( dtor )
408  else
409  callexpr = astNewCALL( dtor, astBuildVtableLookup( dtor, thisexpr ) )
410  end if
411 
412  astNewARG( callexpr, thisexpr )
413 
414  function = callexpr
415 end function
416 
417 function astFakeInstPtr( byval subtype as FBSYMBOL ptr ) as ASTNODE ptr
418  assert( symbIsStruct( subtype ) )
419  function = astNewCONSTi( 0, typeAddrOf( FB_DATATYPE_STRUCT ), subtype )
420 end function
421 
422 function astPatchCtorCall _
423  ( _
424  byval procexpr as ASTNODE ptr, _
425  byval thisexpr as ASTNODE ptr _
426  ) as ASTNODE ptr
427 
428  '' Note: ctors cannot be virtual, so there's no need to worry about
429  '' updating any vtable lookup here (which would use the thisexpr too)
430  assert( astIsCALL( procexpr ) )
431  assert( symbProcGetVtableIndex( procexpr->sym ) = 0 )
432 
433  '' replace the instance pointer
434  astReplaceInstanceArg( procexpr, thisexpr )
435 
436  function = procexpr
437 end function
438 
439 function astCALLCTORToCALL _
440  ( _
441  byval n as ASTNODE ptr _
442  ) as ASTNODE ptr
443 
444  dim as FBSYMBOL ptr sym = any
445  dim as ASTNODE ptr procexpr = any
446 
447  assert( astIsCALLCTOR( n ) )
448 
449  sym = astGetSymbol( n->r )
450 
451  '' the function call is in the left leaf
452  procexpr = n->l
453 
454  '' Update the CALL: Replace the old THIS ptr ARG with a NULL ptr (given
455  '' BYVAL to the BYREF THIS param), since the temp var will be deleted.
456  assert( symbGetType( sym ) = FB_DATATYPE_STRUCT )
457  astReplaceInstanceArg( procexpr, astFakeInstPtr( symbGetSubtype( sym ) ), FB_PARAMMODE_BYVAL )
458 
459  '' remove right leaf (the VAR access on the temp var)
460  astDelTree( n->r )
461 
462  '' if the temp has a dtor it was added to the dtor list,
463  '' remove it too
464  astDtorListDel( sym )
465 
466  '' Delete the temp var itself
467  symbDelSymbol( sym )
468 
469  '' remove the CALLCTOR node
470  astDelNode( n )
471 
472  function = procexpr
473 end function
474 
475 function astBuildImplicitCtorCall _
476  ( _
477  byval subtype as FBSYMBOL ptr, _
478  byval expr as ASTNODE ptr, _
479  byval arg_mode as FB_PARAMMODE, _
480  byref is_ctorcall as integer _
481  ) as ASTNODE ptr
482 
483  dim as integer err_num = any
484  dim as FBSYMBOL ptr proc = any
485 
486  proc = symbFindCtorOvlProc( subtype, expr, arg_mode, @err_num )
487  if( proc = NULL ) then
488  is_ctorcall = FALSE
489 
490  if( err_num <> FB_ERRMSG_OK ) then
491  errReportParam( symbGetCompCtorHead( subtype ), 0, NULL, err_num )
492  return NULL
493  end if
494 
495  '' could be a shallow copy..
496  return expr
497  end if
498 
499  '' check visibility
500  if( symbCheckAccess( proc ) = FALSE ) then
501  errReport( FB_ERRMSG_NOACCESSTOCTOR )
502  end if
503 
504  '' build a ctor call
505  dim as ASTNODE ptr procexpr = astNewCALL( proc )
506 
507  '' Use a fake THIS ptr for now,
508  '' a NULL ptr given BYVAL to the BYREF THIS param
509  astNewARG( procexpr, astFakeInstPtr( subtype ), , FB_PARAMMODE_BYVAL )
510 
511  astNewARG( procexpr, expr, , arg_mode )
512 
513  '' add the optional params, if any
514  dim as integer params = symbGetProcParams( proc ) - 2
515  do while( params > 0 )
516  astNewARG( procexpr, NULL )
517  params -= 1
518  loop
519 
520  is_ctorcall = TRUE
521  function = procexpr
522 
523 end function
524 
525 '':::::
527  ( _
528  byval sym as FBSYMBOL ptr, _
529  byval expr as ASTNODE ptr, _
530  byval arg_mode as FB_PARAMMODE, _
531  byref is_ctorcall as integer _
532  ) as ASTNODE ptr
533 
534  dim as FBSYMBOL ptr subtype = any
535 
536  subtype = symbGetSubType( sym )
537 
538  '' check ctor call
539  if( astIsCALLCTOR( expr ) ) then
540  if( symbGetSubtype( expr ) = subtype ) then
541  is_ctorcall = TRUE
542  '' remove the the anon/temp instance
543  return astCALLCTORToCALL( expr )
544  end if
545  end if
546 
547  '' try calling any ctor with the expression
548  function = astBuildImplicitCtorCall( subtype, expr, arg_mode, is_ctorcall )
549 
550 end function
551 
552 ''
553 '' procs
554 ''
555 
556 function astBuildProcAddrof( byval proc as FBSYMBOL ptr ) as ASTNODE ptr
557  symbSetIsAccessed( proc )
558  function = astNewADDROF( astNewVAR( proc ) )
559 end function
560 
561 '' For accessing the function result from within the function
562 function astBuildProcResultVar _
563  ( _
564  byval proc as FBSYMBOL ptr, _
565  byval res as FBSYMBOL ptr _
566  ) as ASTNODE ptr
567 
568  '' proc returns UDT in hidden byref UDT param?
569  if( symbProcReturnsOnStack( proc ) ) then
570  function = astNewDEREF( astNewVAR( res, 0, typeAddrOf( FB_DATATYPE_STRUCT ), symbGetSubtype( res ) ) )
571  else
572  function = astNewVAR( res )
573  end if
574 
575 end function
576 
577 ''
578 '' instance ptr
579 ''
580 
581 '':::::
582 function astBuildInstPtr _
583  ( _
584  byval sym as FBSYMBOL ptr, _
585  byval fld as FBSYMBOL ptr, _
586  byval idxexpr as ASTNODE ptr _
587  ) as ASTNODE ptr
588 
589  dim as ASTNODE ptr expr = any
590  dim as integer dtype = any
591  dim as FBSYMBOL ptr subtype = any
592  dim as longint ofs = any
593 
594  dtype = symbGetFullType( sym )
595  subtype = symbGetSubtype( sym )
596 
597  '' it's always a param
598  expr = astNewVAR( sym, 0, typeAddrOf( dtype ), subtype )
599 
600  if( fld <> NULL ) then
601  dtype = symbGetFullType( fld )
602  subtype = symbGetSubtype( fld )
603 
604  '' build sym.field( index )
605 
606  ofs = symbGetOfs( fld )
607  if( ofs <> 0 ) then
608  expr = astNewBOP( AST_OP_ADD, expr, astNewCONSTi( ofs ) )
609  end if
610 
611  '' array access?
612  if( idxexpr <> NULL ) then
613  '' times length
614  expr = astNewBOP( AST_OP_ADD, expr, _
615  astNewBOP( AST_OP_MUL, idxexpr, _
616  astNewCONSTi( symbGetLen( fld ) ) ) )
617  end if
618 
619  end if
620 
621  expr = astNewDEREF( expr, dtype, subtype )
622 
623  if( fld <> NULL ) then
624  expr = astNewFIELD( expr, fld )
625  end if
626 
627  function = expr
628 end function
629 
630 function astBuildInstPtrAtOffset _
631  ( _
632  byval sym as FBSYMBOL ptr, _
633  byval fld as FBSYMBOL ptr, _
634  byval ofs as longint _
635  ) as ASTNODE ptr
636 
637  dim as ASTNODE ptr expr = any
638  dim as integer dtype = any
639  dim as FBSYMBOL ptr subtype = any
640 
641  dtype = symbGetFullType( sym )
642  subtype = symbGetSubtype( sym )
643 
644  '' THIS is a BYREF AS UDT parameter, the typeAddrOf() is needed to
645  '' make the expression be an UDT PTR.
646  expr = astNewVAR( sym, 0, typeAddrOf( dtype ), subtype )
647 
648  if( fld <> NULL ) then
649  dtype = symbGetFullType( fld )
650  subtype = symbGetSubtype( fld )
651  end if
652 
653  if( ofs <> 0 ) then
654  expr = astNewBOP( AST_OP_ADD, expr, astNewCONSTi( ofs ) )
655  end if
656 
657  expr = astNewDEREF( expr, dtype, subtype )
658 
659  if( fld <> NULL ) then
660  expr = astNewFIELD( expr, fld )
661  end if
662 
663  function = expr
664 end function
665 
666 ''
667 '' misc
668 ''
669 
670 '':::::
671 function astBuildTypeIniCtorList _
672  ( _
673  byval sym as FBSYMBOL ptr _
674  ) as ASTNODE ptr
675 
676  dim as ASTNODE ptr tree
677 
678  tree = astTypeIniBegin( symbGetFullType( sym ), symbGetSubtype( sym ), TRUE )
679 
680  astTypeIniAddCtorList( tree, sym, symbGetArrayElements( sym ) )
681 
682  astTypeIniEnd( tree, TRUE )
683 
684  function = tree
685 
686 end function
687 
688 '':::::
689 function astBuildMultiDeref _
690  ( _
691  byval cnt as integer, _
692  byval expr as ASTNODE ptr, _
693  byval dtype as integer, _
694  byval subtype as FBSYMBOL ptr _
695  ) as ASTNODE ptr
696 
697  do while( cnt > 0 )
698  if( typeIsPtr( dtype ) = FALSE ) then
699  if( symb.globOpOvlTb(AST_OP_DEREF).head = NULL ) then
700  errReport( FB_ERRMSG_EXPECTEDPOINTER, TRUE )
701  exit do
702  end if
703 
704  '' check op overloading
705  dim as FBSYMBOL ptr proc = any
706  dim as FB_ERRMSG err_num = any
707 
708  proc = symbFindUopOvlProc( AST_OP_DEREF, expr, @err_num )
709  if( proc <> NULL ) then
710  '' build a proc call
711  expr = astBuildCall( proc, expr )
712  if( expr = NULL ) then
713  return NULL
714  end if
715 
716  dtype = astGetFullType( expr )
717  subtype = astGetSubType( expr )
718  else
719  errReport( FB_ERRMSG_EXPECTEDPOINTER, TRUE )
720  exit do
721  end if
722  else
723  dtype = typeDeref( dtype )
724 
725  '' incomplete type?
726  select case typeGet( dtype )
727  case FB_DATATYPE_VOID, FB_DATATYPE_FWDREF
728  errReport( FB_ERRMSG_INCOMPLETETYPE, TRUE )
729  '' error recovery: fake a type
730  dtype = FB_DATATYPE_BYTE
731 
732  '' Function pointer?
733  case FB_DATATYPE_FUNCTION
734  '' Disallow dereferencing them with '*', because that would only access
735  '' the function's code, that's not a good idea.
736  '' (This is only a parser check though, using cast() it's still possible)
737  errReport( FB_ERRMSG_TYPEMISMATCH, TRUE )
738  dtype = FB_DATATYPE_BYTE
739 
740  end select
741 
742  '' null pointer checking
743  if( env.clopt.extraerrchk ) then
744  expr = astBuildPTRCHK( expr )
745  end if
746 
747  expr = astNewDEREF( expr, dtype, subtype )
748  end if
749 
750  cnt -= 1
751  loop
752 
753  function = expr
754 
755 end function
756 
757 ''
758 '' arrays
759 ''
760 
761 '':::::
762 function astBuildArrayDescIniTree _
763  ( _
764  byval desc as FBSYMBOL ptr, _
765  byval array as FBSYMBOL ptr, _
766  byval array_expr as ASTNODE ptr _
767  ) as ASTNODE ptr
768 
769  dim as ASTNODE ptr tree = any
770  dim as integer dtype = any, dims = any
771  dim as FBSYMBOL ptr elm = any, dimtb = any, subtype = any
772 
773  '' COMMON or EXTERN? Cannot be initialized
774  if( symbIsCommon( array ) or symbIsExtern( array ) ) then
775  return NULL
776  end if
777 
778  ''
779  tree = astTypeIniBegin( symbGetFullType( desc ), symbGetSubtype( desc ), TRUE )
780 
781  dtype = symbGetFullType( array )
782  subtype = symbGetSubType( array )
783  dims = symbGetArrayDimensions( array )
784 
785  '' note: assuming the arrays descriptors won't be objects with methods
786  elm = symbGetUDTSymbTbHead( symbGetSubtype( desc ) )
787 
788  if( array_expr = NULL ) then
789  if( symbGetIsDynamic( array ) ) then
790  array_expr = astNewCONSTi( 0, typeAddrOf( dtype ), subtype )
791  else
792  array_expr = astNewADDROF( astNewVAR( array ) )
793  end if
794  else
795  array_expr = astNewADDROF( array_expr )
796  end if
797 
798  astTypeIniScopeBegin( tree, NULL )
799 
800  '' .data = @array(0) + diff
801  astTypeIniAddAssign( tree, _
802  astNewBOP( AST_OP_ADD, astCloneTree( array_expr ), _
803  astNewCONSTi( symbGetArrayOffset( array ) ) ), _
804  elm )
805 
806  elm = symbGetNext( elm )
807 
808  '' .ptr = @array(0)
809  astTypeIniAddAssign( tree, array_expr, elm )
810 
811  elm = symbGetNext( elm )
812 
813  '' .size = len( array ) * elements( array )
814  astTypeIniAddAssign( tree, _
815  astNewCONSTi( symbGetLen( array ) * symbGetArrayElements( array ) ), _
816  elm )
817 
818  elm = symbGetNext( elm )
819 
820  '' .element_len = len( array )
821  astTypeIniAddAssign( tree, astNewCONSTi( symbGetLen( array ) ), elm )
822 
823  elm = symbGetNext( elm )
824 
825  '' .dimensions = dims( array )
826  '' If the dimension count is unknown, store 0 as dimension count,
827  '' since it's an unallocated dynamic array.
828  astTypeIniAddAssign( tree, astNewCONSTi( iif( dims = -1, 0, dims ) ), elm )
829 
830  elm = symbGetNext( elm )
831 
832  '' setup dimTB
833  dimtb = symbGetUDTSymbTbHead( symbGetSubtype( elm ) )
834 
835  astTypeIniScopeBegin( tree, NULL )
836 
837  '' static array?
838  if( symbGetIsDynamic( array ) = FALSE ) then
839  assert( dims <> -1 )
840 
841  dim as FBVARDIM ptr d
842 
843  d = symbGetArrayFirstDim( array )
844  do while( d <> NULL )
845  elm = dimtb
846 
847  astTypeIniScopeBegin( tree, NULL )
848 
849  '' .elements = (ubound( array, d ) - lbound( array, d )) + 1
850  astTypeIniAddAssign( tree, astNewCONSTi( d->upper - d->lower + 1 ), elm )
851 
852  elm = symbGetNext( elm )
853 
854  '' .lbound = lbound( array, d )
855  astTypeIniAddAssign( tree, astNewCONSTi( d->lower ), elm )
856 
857  elm = symbGetNext( elm )
858 
859  '' .ubound = ubound( array, d )
860  astTypeIniAddAssign( tree, astNewCONSTi( d->upper ), elm )
861 
862  astTypeIniScopeEnd( tree, NULL )
863 
864  d = d->next
865  loop
866 
867  '' dynamic..
868  else
869  '' If the dimension count is unknown, we actually reserved room
870  '' for the max amount
871  if( dims = -1 ) then
872  dims = FB_MAXARRAYDIMS
873  end if
874 
875  '' Clear all dimTB entries
876  astTypeIniAddPad( tree, dims * symbGetLen( symb.fbarraydim ) )
877  end if
878 
879  astTypeIniScopeEnd( tree, NULL )
880 
881  ''
882  astTypeIniScopeEnd( tree, NULL )
883 
884  astTypeIniEnd( tree, TRUE )
885 
886  ''
887  symbSetIsInitialized( desc )
888 
889  function = tree
890 
891 end function
892 
893 function hConstBound _
894  ( _
895  byval arrayexpr as ASTNODE ptr, _
896  byval dimexpr as ASTNODE ptr, _
897  byval is_lbound as integer _
898  ) as ASTNODE ptr
899 
900  dim as FBSYMBOL ptr array = any
901  dim as FBVARDIM ptr d = any
902  dim as integer dimension = any
903  dim as longint bound = any
904 
905  function = NULL
906 
907  '' We must know the array symbol, it's carrying the bounds information
908  select case( arrayexpr->class )
909  case AST_NODECLASS_VAR, AST_NODECLASS_FIELD
910 
911  case else
912  exit function
913  end select
914 
915  array = arrayexpr->sym
916  if( array = NULL ) then
917  exit function
918  end if
919 
920  '' It must be a fixed-size array
921  if( symbIsDynamic( array ) or symbIsParamBydesc( array ) ) then
922  exit function
923  end if
924 
925  '' The dimension argument must be constant
926  if( astIsCONST( dimexpr ) = FALSE ) then
927  exit function
928  end if
929 
930  '' dimension is 1-based
931  assert( astGetDataType( dimexpr ) = FB_DATATYPE_INTEGER )
932  dimension = astConstGetInt( dimexpr )
933 
934  '' Find the referenced dimension
935  if( dimension >= 1 ) then
936  d = symbGetArrayFirstDim( array )
937  while( (d <> NULL) and (dimension > 1) )
938  dimension -= 1
939  d = d->next
940  wend
941  else
942  d = NULL
943  end if
944 
945  if( d ) then
946  if( is_lbound ) then
947  bound = d->lower
948  else
949  '' Ellipsis ubound? can happen if ubound() is used in
950  '' an array initializer, when the ubound isn't fully
951  '' known yet, e.g. in this case:
952  '' dim array(0 to ...) as integer = { 1, ubound( array ), 3 }
953  if( d->upper = FB_ARRAYDIM_UNKNOWN ) then
954  exit function
955  end if
956  bound = d->upper
957  end if
958  else
959  '' Out-of-bounds dimension argument: For dimension = 0 we
960  '' return l/ubound of the array's dimTB, with lbound=1 and
961  '' ubound=dimensions. For other out-of-bound dimension values,
962  '' we return lbound=0 and ubound=-1.
963  if( dimension = 0 ) then
964  bound = iif( is_lbound, 1, symbGetArrayDimensions( array ) )
965  else
966  bound = iif( is_lbound, 0, -1 )
967  end if
968  end if
969 
970  function = astNewCONSTi( bound )
971 end function
972 
973 function astBuildArrayBound _
974  ( _
975  byval arrayexpr as ASTNODE ptr, _
976  byval dimexpr as ASTNODE ptr, _
977  byval tk as integer _
978  ) as ASTNODE ptr
979 
980  dim as ASTNODE ptr expr = any
981 
982  '' Ensure it's an INTEGER, show overflow warnings
983  '' (normally astNewARG() would do it, but only for the runtime version
984  '' of course, not when evaluated at compile-time)
985  errPushParamLocation( NULL, tk, 2, "dimension" )
986  dimexpr = astNewCONV( FB_DATATYPE_INTEGER, NULL, dimexpr )
988 
989  '' Try to evaluate l/ubound( array, dimension ) at compile-time
990  expr = hConstBound( arrayexpr, dimexpr, (tk = FB_TK_LBOUND) )
991 
992  if( expr = NULL ) then
993  '' Fall back to run-time ubound(), that will work for array
994  '' declarations that can have non-const initializers, and cause
995  '' an error if a constant initializer was expected.
996  expr = rtlArrayBound( arrayexpr, dimexpr, (tk = FB_TK_LBOUND) )
997  end if
998 
999  function = expr
1000 end function
1001 
1002 ''
1003 '' strings
1004 ''
1005 
1006 function astBuildStrPtr( byval lhs as ASTNODE ptr ) as ASTNODE ptr
1007  '' note: only var-len strings expressions should be passed
1008  dim as ASTNODE ptr expr = any
1009 
1010  '' *cast( zstring ptr ptr, @lhs )
1011  expr = astNewDEREF( astNewCONV( typeMultAddrOf( FB_DATATYPE_CHAR, 2 ), NULL, _
1012  astNewADDROF( lhs ) ) )
1013 
1014  '' HACK: make it return an immutable value by returning (expr + 0)
1015  '' in order to prevent things like STRPTR(s) = 0
1016  '' (TODO: find a better way of doing this?)
1017  expr = astNewBOP( AST_OP_ADD, expr, astNewCONSTi( 0 ), NULL )
1018 
1019  return expr
1020 end function
1021