FreeBASIC  0.91.0
ast-node-arg.bas
Go to the documentation of this file.
1 '' AST function argument nodes
2 '' l = expression; r = next argument
3 ''
4 '' chng: sep/2004 written [v1ctor]
5 
6 
7 #include once "fb.bi"
8 #include once "fbint.bi"
9 #include once "list.bi"
10 #include once "ir.bi"
11 #include once "rtl.bi"
12 #include once "ast.bi"
13 
14 '':::::
15 function hAllocTmpArrayDesc _
16  ( _
17  byval array as FBSYMBOL ptr, _
18  byval array_expr as ASTNODE ptr, _
19  byref tree as ASTNODE ptr _
20  ) as FBSYMBOL ptr
21 
22  dim as FBSYMBOL ptr desc = any
23 
24  '' create
25  desc = symbAddArrayDesc( array, symbGetArrayDimensions( array ) )
26 
27  '' don't let NewDECL() fill it
28  symbGetTypeIniTree( desc ) = astBuildArrayDescIniTree( desc, _
29  array, _
30  array_expr )
31 
32 
33 
34  '' declare
35  tree = astNewDECL( desc, (symbGetTypeIniTree( desc ) = NULL) )
36 
37  '' flush (see symbAddArrayDesc(), the desc can't never be static)
38  tree = astNewLINK( tree, _
39  astTypeIniFlush( symbGetTypeIniTree( desc ), _
40  desc, _
41  AST_INIOPT_ISINI ) )
42 
43  symbSetTypeIniTree( desc, NULL )
44 
45  function = desc
46 
47 end function
48 
49 '':::::
50 function hTmpStrListAdd _
51  ( _
52  byval parent as ASTNODE ptr, _
53  byval n as ASTNODE ptr, _
54  byval dtype as integer, _
55  byval copyback as integer _
56  ) as AST_TMPSTRLIST_ITEM ptr
57 
58  dim as AST_TMPSTRLIST_ITEM ptr t = any
59  dim as FBSYMBOL ptr s = any
60 
61  '' alloc a node
62  t = listNewNode( @ast.call.tmpstrlist )
63 
64  t->prev = parent->call.strtail
65  parent->call.strtail = t
66 
67  s = symbAddTempVar( dtype )
68 
69  t->sym = s
70  if( copyback ) then
71  t->srctree = astOptimizeTree( astCloneTree( n ) )
72  else
73  t->srctree = NULL
74  end if
75 
76  function = t
77 
78 end function
79 
80 '':::::
81 function hAllocTmpString _
82  ( _
83  byval parent as ASTNODE ptr, _
84  byval n as ASTNODE ptr, _
85  byval copyback as integer _
86  ) as ASTNODE ptr
87 
88  dim as AST_TMPSTRLIST_ITEM ptr t = any
89 
90  '' create temp string to pass as parameter
91  t = hTmpStrListAdd( parent, n, FB_DATATYPE_STRING, copyback )
92 
93  '' temp string = src string
94  function = astNewLINK( _
95  astNewLINK( _
96  astBuildTempVarClear( t->sym ), _
97  rtlStrAssign( astNewVAR( t->sym ), n ), _
98  FALSE ), _
99  astNewVAR( t->sym ), _
100  FALSE )
101 
102 end function
103 
104 '':::::
105 function hAllocTmpWstrPtr _
106  ( _
107  byval parent as ASTNODE ptr, _
108  byval n as ASTNODE ptr _
109  ) as ASTNODE ptr
110 
111  dim as AST_TMPSTRLIST_ITEM ptr t = any
112 
113  '' create temp wstring ptr to pass as parameter
114  t = hTmpStrListAdd( parent, NULL, typeAddrOf( FB_DATATYPE_WCHAR ), FALSE )
115 
116  '' evil hack: a function returning a "wstring" is actually returning a pointer,
117  '' but NewAssign() shouldn't copy the string, just the pointer
118  astSetType( n, typeAddrOf( FB_DATATYPE_WCHAR ), NULL )
119 
120  '' temp string = src string
121  function = astNewASSIGN( astNewVAR( t->sym ), n )
122 end function
123 
124 '':::::
125 function hCheckStringArg _
126  ( _
127  byval parent as ASTNODE ptr, _
128  byval param as FBSYMBOL ptr, _
129  byval arg as ASTNODE ptr _
130  ) as ASTNODE ptr
131 
132  dim as integer arg_dtype = any, copyback = any
133 
134  function = arg
135 
136  arg_dtype = astGetDatatype( arg )
137 
138  '' calling the runtime lib?
139  if( parent->call.isrtl ) then
140 
141  '' passed byref?
142  if( symbGetParamMode( param ) = FB_PARAMMODE_BYREF ) then
143 
144  select case arg_dtype
145  '' var-len param: all rtlib procs will free the
146  '' temporary strings and descriptors automatically
147  case FB_DATATYPE_STRING
148  exit function
149 
150  '' wstring? convert and let rtl to free the temp
151  '' var-len result..
152  case FB_DATATYPE_WCHAR
153  return hAllocTmpString( parent, arg, FALSE )
154 
155  '' anything else, just alloc a temp descriptor (assuming
156  '' here that no rtlib function will EVER change the
157  '' strings passed as param)
158  case else
159  return rtlStrAllocTmpDesc( arg )
160  end select
161 
162  '' passed byval..
163  else
164 
165  '' var-len?
166  select case arg_dtype
167  case FB_DATATYPE_STRING
168  '' not a temp var-len returned by functions? skip..
169  if( arg->class <> AST_NODECLASS_CALL ) then
170  exit function
171  end if
172 
173  '' wstring? convert and add it delete list or the
174  '' temp var-len result would leak
175  case FB_DATATYPE_WCHAR
176  '' let hAllocTmpString() do it..
177 
178  '' anything else, do nothing..
179  case else
180  exit function
181  end select
182 
183  '' create temp string to pass as parameter
184  return hAllocTmpString( parent, arg, FALSE )
185 
186  end if
187 
188  end if
189 
190  '' it's not a rtl function.. var-len strings won't be automatically
191  '' removed nor it's safe to pass non fixed-len strings to var-len
192  '' params as they can be modified inside the callee function..
193  copyback = FALSE
194 
195  select case symbGetParamMode( param )
196  '' passed by reference?
197  case FB_PARAMMODE_BYREF
198 
199  select case arg_dtype
200  '' fixed-length?
201  case FB_DATATYPE_FIXSTR
202  '' byref arg and fixed-len param: alloc a temp string, copy
203  '' fixed to temp and pass temp
204  '' (ast will have to copy temp back to fixed when function
205  '' returns and delete temp)
206 
207  '' don't copy back if it's a function returning a fixed-len
208  if( arg->class <> AST_NODECLASS_CALL ) then
209  copyback = TRUE
210  end if
211 
212  '' var-len?
213  case FB_DATATYPE_STRING
214  '' if not a function's result, skip..
215  if( arg->class <> AST_NODECLASS_CALL ) then
216  exit function
217  end if
218 
219  '' wstring? it must be converted and the temp var-len result
220  '' have to be deleted when the function return
221  case FB_DATATYPE_WCHAR
222  '' let hAllocTmpString() do it..
223 
224  '' anything else..
225  case else
226  '' byref arg and byte/w|zstring/ptr param: alloc a temp
227  '' string, copy byte ptr to temp and pass temp
228 
229  end select
230 
231  '' passed by value?
232  case FB_PARAMMODE_BYVAL
233 
234  select case arg_dtype
235  '' var-len?
236  case FB_DATATYPE_STRING
237 
238  '' not a temp var-len function result? do nothing..
239  if( arg->class <> AST_NODECLASS_CALL ) then
240  exit function
241  end if
242 
243  '' wstring? it must be converted and the temp var-len result
244  '' have to be deleted when the function return
245  case FB_DATATYPE_WCHAR
246  '' let hAllocTmpString() do it..
247 
248  '' anything else, do nothing..
249  case else
250  exit function
251  end select
252 
253  end select
254 
255  '' create temp string to pass as parameter
256  function = hAllocTmpString( parent, arg, copyback )
257 
258 end function
259 
260 '':::::
262  ( _
263  byval parent as ASTNODE ptr, _
264  byval n as ASTNODE ptr, _
265  byval checkrtl as integer _
266  )
267 
268  dim as ASTNODE ptr arg = n->l
269  dim as integer arg_dtype = astGetDatatype( arg )
270 
271  if( checkrtl = FALSE ) then
272  '' rtl? don't mess..
273  if( parent->call.isrtl ) then
274  return
275  end if
276  end if
277 
278  '' var- or fixed-len string param?
279  if( typeGetClass( arg_dtype ) = FB_DATACLASS_STRING ) then
280  '' if it's a function returning a STRING, it will have to be
281  '' deleted automagically when the proc being called return
282  if( astIsCALL( arg ) ) then
283  '' create a temp string to pass as parameter (no copy is
284  '' done at rtlib, as the returned string is a temp too)
285  n->l = hAllocTmpString( parent, arg, FALSE )
286  arg_dtype = FB_DATATYPE_STRING
287  end if
288 
289  '' not fixed-len? deref var-len
290  if( arg_dtype <> FB_DATATYPE_FIXSTR ) then
291  n->l = astBuildStrPtr( n->l )
292  '' fixed-len..
293  else
294  '' get the address of
295  n->l = astNewCONV( typeAddrOf( FB_DATATYPE_CHAR ), _
296  NULL, _
297  astNewADDROF( n->l ) )
298  end if
299 
300  astGetFullType( n ) = astGetFullType( astGetLeft( n ) )
301 
302  '' w- or z-string
303  else
304  select case arg_dtype
305  '' zstring? take the address of
306  case FB_DATATYPE_CHAR
307  n->l = astNewADDROF( arg )
308  astGetFullType( n ) = astGetFullType( astGetLeft( n ) )
309 
310  '' wstring?
311  case FB_DATATYPE_WCHAR
312 
313  '' if it's a function returning a WSTRING, it will have to be
314  '' deleted automatically when the proc being called return
315  if( astIsCALL( arg ) ) then
316  n->l = hAllocTmpWstrPtr( parent, arg )
317 
318  '' not a temporary..
319  else
320  '' take the address of
321  n->l = astNewADDROF( arg )
322  end if
323 
324  astGetFullType( n ) = astGetFullType( astGetLeft( n ) )
325  end select
326  end if
327 end sub
328 
329 sub hBuildByrefArg _
330  ( _
331  byval param as FBSYMBOL ptr, _
332  byval n as ASTNODE ptr, _
333  byval arg as ASTNODE ptr _
334  )
335 
336  arg = astNewADDROF( arg )
337  arg = astNewCONV( typeAddrOf( symbGetFullType( param ) ), symbGetSubtype( param ), arg )
338  assert( arg )
339 
340  n->l = arg
341  n->arg.mode = FB_PARAMMODE_BYVAL
342 
343 end sub
344 
345 sub hCheckByrefParam _
346  ( _
347  byval param as FBSYMBOL ptr, _
348  byval n as ASTNODE ptr _
349  )
350 
351  dim as ASTNODE ptr arg = n->l
352 
353  '' skip any casting if they won't do any conversion
354  dim as ASTNODE ptr t = arg
355  if( arg->class = AST_NODECLASS_CONV ) then
356  if( arg->cast.doconv = FALSE ) then
357  t = arg->l
358  end if
359  end if
360 
361  select case as const t->class
362  '' var, array index or pointer? pass as-is (assuming the type was already checked)
363  case AST_NODECLASS_VAR, AST_NODECLASS_IDX, _
364  AST_NODECLASS_FIELD, AST_NODECLASS_DEREF, _
365  AST_NODECLASS_IIF
366 
367  case else
368  '' string? do nothing (ie: functions returning var-len string's)
369  select case as const( astGetDataType( arg ) )
370  case FB_DATATYPE_STRING, FB_DATATYPE_FIXSTR, _
371  FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
372  return
373 
374  '' UDT? do nothing, just take the address of
375  case FB_DATATYPE_STRUCT
376 
377  case else
378  '' scalars: store arg to a temp var and pass it
379  arg = astNewASSIGN( astNewVAR( symbAddTempVar( arg->dtype, arg->subtype ) ), _
380  arg, AST_OPOPT_DONTCHKPTR )
381  end select
382 
383  end select
384 
385  '' take the address of
386  hBuildByrefArg( param, n, arg )
387 end sub
388 
389 '':::::
390 function hCheckByDescParam _
391  ( _
392  byval parent as ASTNODE ptr, _
393  byval param as FBSYMBOL ptr, _
394  byval n as ASTNODE ptr _
395  ) as integer
396 
397  dim as ASTNODE ptr arg = n->l, desc_tree = any
398  dim as integer arg_dtype = astGetDatatype( arg ), sym_dtype = any
399 
400  '' is arg a pointer?
401  if( n->arg.mode = FB_PARAMMODE_BYVAL ) then
402  return TRUE
403  end if
404 
405  dim as FBSYMBOL ptr s = any, desc = any
406 
407  s = astGetSymbol( arg )
408 
409  if( s = NULL ) then
410  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
411  return FALSE
412  end if
413 
414  sym_dtype = symbGetType( param )
415 
416  '' same type? (don't check if it's a rtl proc, or a forward call)
417  if( (parent->call.isrtl = FALSE) and (sym_dtype <> FB_DATATYPE_VOID) ) then
418  if( (typeGetClass( arg_dtype ) <> typeGetClass( sym_dtype )) or _
419  (typeGetSize( arg_dtype ) <> typeGetSize( sym_dtype )) ) then
420  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
421  return FALSE
422  end if
423  end if
424 
425  '' type field?
426  if( symbGetClass( s ) = FB_SYMBCLASS_FIELD ) then
427  '' not an array?
428  if( symbGetArrayDimensions( s ) = 0 ) then
429  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
430  return FALSE
431  end if
432 
433  '' create a temp array descriptor
434  desc = hAllocTmpArrayDesc( s, arg, desc_tree )
435 
436  else
437  '' an argument passed by descriptor?
438  if( symbIsParamByDesc( s ) ) then
439  '' it's a pointer, but it will be seen as anything else
440  '' (ie: "array() as string"), so, remap the type
441  astDelTree( arg )
442  n->l = astNewVAR( s, 0, typeAddrOf( FB_DATATYPE_VOID ) )
443  return TRUE
444  end if
445 
446  '' it's a var? !!!WRITEME!!! (this probably needs to change
447  '' if functions return arrays...)
448  if( symbIsVar( s ) ) then
449  '' not an array?
450  desc = symbGetArrayDescriptor( s )
451  if( desc = NULL ) then
452  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
453  return FALSE
454  end if
455  else
456  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
457  return FALSE
458  end if
459 
460  desc_tree = NULL
461 
462  '' remove node
463  astDelTree( arg )
464  end if
465 
466  '' create a new
467  n->l = astNewLINK( _
468  astNewCONV( typeAddrOf( FB_DATATYPE_VOID ), NULL, _
469  astNewADDROF( astNewVAR( desc ) ) ), _
470  desc_tree )
471 
472  function = TRUE
473 
474 end function
475 
476 '':::::
477 function hCheckVarargParam _
478  ( _
479  byval parent as ASTNODE ptr, _
480  byval param as FBSYMBOL ptr, _
481  byval n as ASTNODE ptr _
482  ) as integer
483 
484  dim as ASTNODE ptr arg = n->l
485  dim as integer arg_dtype = astGetDatatype( arg )
486 
487  select case as const typeGetClass( arg_dtype )
488  '' var-len string? check..
489  case FB_DATACLASS_STRING
490  hStrArgToStrPtrParam( parent, n, FALSE )
491 
492  case FB_DATACLASS_INTEGER
493  select case arg_dtype
494  '' w|zstring? ditto..
495  case FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
496  hStrArgToStrPtrParam( parent, n, FALSE )
497 
498  case else
499  '' if < sizeof(int), convert to int (C ABI)
500  '' Even for 64bit, varargs are promoted to 32bit int.
501  if( typeGetSize( arg_dtype ) < 4 ) then
502  n->l = astNewCONV( iif( typeIsSigned( arg_dtype ), _
503  FB_DATATYPE_LONG, _
504  FB_DATATYPE_ULONG ), _
505  NULL, arg )
506  end if
507  end select
508 
509  case FB_DATACLASS_FPOINT
510  '' float? convert it to double (C ABI)
511  if( arg_dtype = FB_DATATYPE_SINGLE ) then
512  n->l = astNewCONV( FB_DATATYPE_DOUBLE, NULL, arg )
513  end if
514 
515  case else
516  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
517  return FALSE
518  end select
519 
520  function = TRUE
521 
522 end function
523 
524 '':::::
525 sub hCheckVoidParam _
526  ( _
527  byval parent as ASTNODE ptr, _
528  byval param as FBSYMBOL ptr, _
529  byval n as ASTNODE ptr _
530  )
531 
532  dim as ASTNODE ptr arg = n->l
533 
534  if( n->arg.mode = FB_PARAMMODE_BYVAL ) then
535  '' check strings passed BYVAL
536  hStrArgToStrPtrParam( parent, n, FALSE )
537  return
538  end if
539 
540  '' another quirk: constants, pass byval even if BYVAL wasn't given
541  if( env.clopt.lang <> FB_LANG_QB ) then
542  if( astIsCONST( arg ) or astIsOFFSET( arg ) ) then
543  return
544  end if
545  end if
546 
547  '' pass BYREF, check if a temp param isn't needed
548  hCheckByrefParam( param, n )
549 end sub
550 
551 '':::::
552 function hCheckStrParam _
553  ( _
554  byval parent as ASTNODE ptr, _
555  byval param as FBSYMBOL ptr, _
556  byval n as ASTNODE ptr _
557  ) as integer
558 
559  dim as ASTNODE ptr arg = n->l
560  dim as integer arg_dtype = astGetDatatype( arg )
561 
562  '' check arg type
563  select case as const arg_dtype
564  case FB_DATATYPE_STRING, FB_DATATYPE_FIXSTR
565 
566  '' a z|wstring?
567  case FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
568 
569  '' not a string?
570  case else
571  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
572  return FALSE
573  end select
574 
575  '' byval and variable:
576  '' pass the pointer at ofs 0 of the string descriptor
577  '' byval and fixed/zstring:
578  '' pass the pointer as-is
579  '' byval and wstring
580  '' same as above but convert to ascii first
581 
582  '' byref and variable:
583  '' pass the pointer to descriptor
584  '' byref and fixed/zstring:
585  '' alloc a temp string, copy fixed to temp, pass temp,
586  '' copy temp back to fixed when func returns, del temp
587  '' byref and wstring
588  '' same as above but convert to ascii first
589 
590  '' alloc a temp string if needed
591  arg = hCheckStringArg( parent, param, arg )
592  n->l = arg
593 
594  '' byval param?
595  if( symbGetParamMode( param ) = FB_PARAMMODE_BYVAL ) then
596  '' var-len? deref..
597  if( arg_dtype = FB_DATATYPE_STRING ) then
598  n->l = astBuildStrPtr( arg )
599  return TRUE
600  end if
601  end if
602 
603  '' if it's a function returning a STRING, it's actually a pointer
604  if( arg->class <> AST_NODECLASS_CALL ) then
605  n->l = astNewADDROF( arg )
606  end if
607 
608  function = TRUE
609 
610 end function
611 
612 sub hByteByByte( byval param as FBSYMBOL ptr, byval n as ASTNODE ptr )
613  '' UDT in memory, push byte-by-byte, by setting ASTNODE.arg.lgt,
614  '' telling irEmitPUSHARG() to push this arg to stack byte-by-byte.
615  '' Note: No rounding, to prevent overruns in the ASM
616  n->arg.lgt = symbGetLen( symbGetSubtype( param ) )
617 end sub
618 
619 sub hUDTPassByval _
620  ( _
621  byval param as FBSYMBOL ptr, _
622  byval n as ASTNODE ptr _
623  )
624 
625  dim as FBSYMBOL ptr tmp = any
626  dim as ASTNODE ptr arg = any, callexpr = any
627  dim as integer is_ctorcall = any
628 
629  arg = n->l
630 
631  '' no dtor, copy-ctor or virtual members?
632  if( symbCompIsTrivial( symbGetSubtype( param ) ) ) then
633  if( astIsCALL( arg ) ) then
634  if( symbProcReturnsOnStack( arg->sym ) ) then
635  '' Returning on stack, access the temp result var
636  n->l = astBuildCallResultVar( arg )
637  hByteByByte( param, n )
638  else
639  assert( symbProcReturnsByref( arg->sym ) = FALSE )
640  '' CALL with result in registers, patch the type
641  astSetType( arg, symbGetProcRealType( arg->sym ), _
642  symbGetProcRealSubtype( arg->sym ) )
643  end if
644  else
645  '' not a CALL, so it must be an UDT in memory
646  hByteByByte( param, n )
647  end if
648 
649  exit sub
650  end if
651 
652  '' Non-trivial type, pass a pointer to a temp copy (implicit BYREF)
653  tmp = symbAddTempVar( symbGetFullType( param ), symbGetSubtype( param ) )
654  astDtorListAdd( tmp )
655 
656  arg = n->l
657 
658  if( astIsTYPEINI( arg ) ) then
659  '' TYPEINI (e.g. from parameter initializer), assign to the temp
660  '' directly (it will probably always be a ctor call too, since
661  '' the parameter initializer wouldn't have allowed anything else)
662  arg = astNewLINK( astTypeIniFlush( arg, tmp, AST_INIOPT_NONE ), astNewVAR( tmp ), FALSE )
663  else
664  '' Otherwise, call a constructor
665  arg = astBuildImplicitCtorCallEx( param, n->l, n->arg.mode, is_ctorcall )
666  if( is_ctorcall ) then
667  '' Wrap in a CALLCTOR again just for fun
668  arg = astNewCALLCTOR( astPatchCtorCall( arg, astNewVAR( tmp ) ), astNewVAR( tmp ) )
669  else
670  '' Shallow copy, and return a VAR access on the temp var
671  arg = astNewLINK( astNewASSIGN( astNewVAR( tmp ), arg ), astNewVAR( tmp ), FALSE )
672  end if
673  end if
674 
675  hBuildByrefArg( param, n, arg )
676 
677 end sub
678 
679 function hImplicitCtor _
680  ( _
681  byval param as FBSYMBOL ptr, _
682  byval n as ASTNODE ptr _
683  ) as integer
684 
685  static as integer rec_cnt = 0
686  dim as ASTNODE ptr arg = any
687  dim as FBSYMBOL ptr tmp = any
688  dim as integer is_ctorcall = any
689 
690  if( symbHasCtor( param ) = FALSE ) then
691  exit function
692  end if
693 
694  '' recursion? (astBuildImplicitCtorCallEx() will call astNewARG() with the same expr)
695  if( rec_cnt <> 0 ) then
696  exit function
697  end if
698 
699  '' try calling any ctor with the expression
700  rec_cnt += 1
701  arg = astBuildImplicitCtorCallEx( param, n->l, n->arg.mode, is_ctorcall )
702  rec_cnt -= 1
703 
704  if( is_ctorcall = FALSE ) then
705  '' No implicit construction possible, n->l is not changed
706  exit function
707  end if
708 
709  tmp = symbAddTempVar( symbGetFullType( param ), symbGetSubtype( param ) )
710  astDtorListAdd( tmp )
711 
712  '' Using a CALLCTOR, to allow hUDTPassByval() to reuse this ctor call if possible
713  '' (instead of making another temp copy)
714  n->l = astNewCALLCTOR( astPatchCtorCall( arg, astNewVAR( tmp ) ), astNewVAR( tmp ) )
715 
716  if( symbGetParamMode( param ) = FB_PARAMMODE_BYVAL ) then
717  hUDTPassByval( param, n )
718  else
719  hBuildByrefArg( param, n, n->l )
720  end if
721 
722  function = TRUE
723 end function
724 
725 '':::::
726 function hCheckUDTParam _
727  ( _
728  byval param as FBSYMBOL ptr, _
729  byval n as ASTNODE ptr _
730  ) as integer
731 
732  dim as FBSYMBOL ptr tmp = any
733  dim as ASTNODE ptr arg = n->l
734 
735  '' not another UDT?
736  if( astGetDatatype( arg ) <> FB_DATATYPE_STRUCT ) then
737  if( hImplicitCtor( param, n ) = FALSE ) then
738  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
739  return FALSE
740  end if
741  return TRUE
742  end if
743 
744  '' check for invalid UDT's (different subtypes)
745  if( arg->subtype <> symbGetSubtype( param ) ) then
746  '' param is not a base type of arg?
747  if( symbGetUDTBaseLevel( arg->subtype, symbGetSubtype( param ) ) = 0 ) then
748  '' no ctor in the param's type?
749  if( hImplicitCtor( param, n ) = FALSE ) then
750  '' no cast operator?
751  arg = astNewCONV( symbGetType( param ), symbGetSubtype( param ), arg )
752  if( arg = NULL ) then
753  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
754  return FALSE
755  end if
756  n->l = arg
757  else
758  '' Found matching param.ctor to create param from arg
759  return TRUE
760  end if
761  '' cast to the base type
762  else
763  arg = astNewCONV( symbGetType( param ), symbGetSubtype( param ), arg )
764  n->l = arg
765  end if
766  end if
767 
768  select case symbGetParamMode( param )
769  '' byref param?
770  case FB_PARAMMODE_BYREF
771  if( astIsCALL( arg ) ) then
772  if( symbProcReturnsOnStack( arg->sym ) = FALSE ) then
773  assert( symbProcReturnsByref( arg->sym ) = FALSE )
774 
775  '' Returning in registers, passed to a BYREF param
776  '' Create a temp var and pass that
777  tmp = symbAddTempVar( astGetDatatype( arg ), arg->subtype )
778 
779  '' No need to bother doing astDtorListAdd()
780  assert( symbHasDtor( tmp ) = FALSE )
781 
782  n->l = astNewLINK( astNewADDROF( astBuildVarField( tmp ) ), _
783  astNewASSIGN( astBuildVarField( tmp ), arg, AST_OPOPT_DONTCHKOPOVL ) )
784  n->arg.mode = FB_PARAMMODE_BYVAL
785  return TRUE
786  end if
787  end if
788 
789  hBuildByrefArg( param, n, arg )
790 
791  '' set the length if it's being passed by value
792  case FB_PARAMMODE_BYVAL
793  hUDTPassByval( param, n )
794 
795  end select
796 
797  function = TRUE
798 end function
799 
800 '':::::
801 function hCheckParam _
802  ( _
803  byval parent as ASTNODE ptr, _
804  byval param as FBSYMBOL ptr, _
805  byval n as ASTNODE ptr _
806  ) as integer
807 
808  dim as ASTNODE ptr arg = any
809  dim as integer param_dtype = any, arg_dtype
810 
811  function = FALSE
812 
813  '' string concatenation is delayed for optimization reasons..
814  n->l = astUpdStrConcat( n->l )
815 
816  arg = n->l
817 
818  '' strip the non-type flags
819  param_dtype = symbGetType( param )
820  arg_dtype = astGetDatatype( arg )
821 
822  select case symbGetParamMode( param )
823  '' by descriptor?
824  case FB_PARAMMODE_BYDESC
825  return hCheckByDescParam( parent, param, n )
826 
827  '' vararg?
828  case FB_PARAMMODE_VARARG
829  return hCheckVarargParam( parent, param, n )
830 
831  case FB_PARAMMODE_BYREF
832  '' as any?
833  if( param_dtype = FB_DATATYPE_VOID ) then
834  hCheckVoidParam( parent, param, n )
835  return TRUE
836  end if
837 
838  '' passing a BYVAL ptr to an BYREF arg?
839  if( n->arg.mode = FB_PARAMMODE_BYVAL ) then
840  if( (typeGetClass( arg_dtype ) <> FB_DATACLASS_INTEGER) or _
841  (typeGetSize( arg_dtype ) <> env.pointersize) ) then
842  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
843  exit function
844  end if
845  return TRUE
846  end if
847 
848  '' Passing a bitfield arg to a byref param? Can't be allowed,
849  '' @udt.bitfield isn't possible either...
850  if( astGetClass( arg ) = AST_NODECLASS_FIELD ) then
851  if( astGetDataType( astGetLeft( arg ) ) = FB_DATATYPE_BITFIELD ) then
852  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
853  exit function
854  end if
855  end if
856 
857  end select
858 
859  '' UDT arg? convert to param type if possible (including strings)
860  select case arg_dtype
861  case FB_DATATYPE_STRUCT ', FB_DATATYPE_CLASS
862  '' try implicit casting op overloading
863  dim as integer err_num = any
864  dim as FBSYMBOL ptr proc = any
865 
866  proc = symbFindCastOvlProc( symbGetFullType( param ), _
867  symbGetSubtype( param ), _
868  arg, _
869  @err_num )
870 
871  if( proc <> NULL ) then
872  static as integer rec_cnt = 0
873  '' recursion? (astBuildCall() will call newARG with the same expr)
874  if( rec_cnt = 0 ) then
875  '' build a proc call
876  rec_cnt += 1
877  n->l = astBuildCall( proc, arg )
878  rec_cnt -= 1
879 
880  arg = n->l
881  arg_dtype = astGetDatatype( arg )
882  end if
883  end if
884  end select
885 
886  select case param_dtype
887  '' string param?
888  case FB_DATATYPE_STRING, FB_DATATYPE_FIXSTR
889  return hCheckStrParam( parent, param, n )
890 
891  '' z/wstring param?
892  case FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
893  assert( symbGetParamMode( param ) = FB_PARAMMODE_BYREF )
894 
895  '' arg must be a string too (for z/wstring: it doesn't matter
896  '' whether it's a DEREF or not, since DEREF can be handled as
897  '' string just fine)
898  select case( arg_dtype )
899  case FB_DATATYPE_STRING, FB_DATATYPE_FIXSTR, _
900  FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
901  '' Rest will be handled below
902  case else
903  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
904  exit function
905  end select
906 
907  '' UDT param? check if the same, can't convert
908  case FB_DATATYPE_STRUCT
909  return hCheckUDTParam( param, n )
910 
911  end select
912 
913  select case as const arg_dtype
914  '' string arg? check z- and w-string ptr params
915  case FB_DATATYPE_STRING, FB_DATATYPE_FIXSTR, _
916  FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
917 
918  select case param_dtype
919  '' zstring ptr / zstring param?
920  case typeAddrOf( FB_DATATYPE_CHAR ), FB_DATATYPE_CHAR
921  '' if it's a wstring param, convert..
922  if( arg_dtype = FB_DATATYPE_WCHAR ) then
923  n->l = rtlToStr( arg, FALSE )
924  end if
925 
926  '' wstring ptr / wstring?
927  case typeAddrOf( FB_DATATYPE_WCHAR ), FB_DATATYPE_WCHAR
928  '' if it's not a wstring param, convert..
929  if( arg_dtype <> FB_DATATYPE_WCHAR ) then
930  n->l = rtlToWstr( arg )
931  end if
932 
933  case else
934  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
935  exit function
936  end select
937 
938  hStrArgToStrPtrParam( parent, n, TRUE )
939 
940  if( typeIsPtr( param_dtype ) = FALSE ) then
941  n = astNewDEREF( n )
942  arg = n
943  else
944  arg = n->l
945  end if
946 
947  arg_dtype = astGetDatatype( arg )
948 
949  '' UDT? implicit casting failed, can't convert..
950  case FB_DATATYPE_STRUCT ', FB_DATATYPE_CLASS
951  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
952  exit function
953  end select
954 
955  '' enum args are only allowed to be passed enum or int params
956  if( (param_dtype = FB_DATATYPE_ENUM) or (arg_dtype = FB_DATATYPE_ENUM) ) then
957  if( typeGetClass( param_dtype ) <> typeGetClass( arg_dtype ) ) then
958  errReportWarn( FB_WARNINGMSG_IMPLICITCONVERSION )
959  end if
960  end if
961 
962  '' pointer checking
963  if( typeIsPtr( param_dtype ) ) then
964  if( astPtrCheck( symbGetFullType( param ), symbGetSubtype( param ), arg ) = FALSE ) then
965  if( typeIsPtr( arg_dtype ) = FALSE ) then
966  errReportWarn( FB_WARNINGMSG_PASSINGSCALARASPTR )
967  else
968  '' if both are UDT, a base param can't be passed to a derived arg
969  if( typeGetDtOnly( param_dtype ) = FB_DATATYPE_STRUCT and typeGetDtOnly( arg_dtype ) = FB_DATATYPE_STRUCT ) then
970  if( symbGetUDTBaseLevel( symbGetSubtype( param ), astGetSubType( arg ) ) > 0 ) then
971  errReport( FB_ERRMSG_INVALIDDATATYPES )
972  exit function
973  else
974  errReportWarn( FB_WARNINGMSG_PASSINGDIFFPOINTERS )
975  end if
976  else
977  errReportWarn( FB_WARNINGMSG_PASSINGDIFFPOINTERS )
978  end if
979  end if
980  end if
981  elseif( typeIsPtr( arg_dtype ) ) then
982  errReportWarn( FB_WARNINGMSG_PASSINGPTRTOSCALAR )
983  end if
984 
985  '' If types are still different, then try to convert the arg to the
986  '' param's type. This is important for astLoadCALL() which determines
987  '' the number of bytes to push/pop based on the ARG dtypes, and also
988  '' helps the GCC/LLVM backends, which need to emit code with proper
989  '' types to avoid errors/warnings from GCC/LLVM, unlike the ASM backend.
990  if( (param_dtype <> arg_dtype) or (param->subtype <> arg->subtype) ) then
991  '' Cannot pass BYREF if different size/class, but we do allow
992  '' passing INTEGER vars to BYREF AS UINTEGER params etc.
993  if( (typeGetSize( param_dtype ) <> typeGetSize( arg_dtype )) or _
994  (typeGetClass( param_dtype ) <> typeGetClass( arg_dtype )) ) then
995  if( symbGetParamMode( param ) = FB_PARAMMODE_BYREF ) then
996  '' skip any casting if they won't do any conversion
997  dim as ASTNODE ptr t = arg
998  if( arg->class = AST_NODECLASS_CONV ) then
999  if( arg->cast.doconv = FALSE ) then
1000  t = arg->l
1001  end if
1002  end if
1003 
1004  '' param diff than arg can't be passed by ref if it's a var/array/ptr
1005  '' (cannot pass a bytevar (1 byte) to BYREF INTEGER (4 bytes) param,
1006  '' that could cause a segfault)
1007  select case as const t->class
1008  case AST_NODECLASS_VAR, AST_NODECLASS_IDX, _
1009  AST_NODECLASS_FIELD, AST_NODECLASS_DEREF, _
1010  AST_NODECLASS_IIF
1011  errReport( FB_ERRMSG_PARAMTYPEMISMATCHAT )
1012  exit function
1013  end select
1014 
1015  '' If it's an rvalue expression though then it's ok,
1016  '' because it will be stored into a temp var of the
1017  '' same type as the BYREF param. Then that temp var
1018  '' is given to the BYREF param, and then it's safe.
1019  end if
1020  end if
1021 
1022  arg = astNewCONV( symbGetFullType( param ), symbGetSubtype( param ), arg )
1023  if( arg = NULL ) then
1024  errReport( FB_ERRMSG_INVALIDDATATYPES )
1025  exit function
1026  end if
1027  arg_dtype = astGetDatatype( arg )
1028 
1029  n->l = arg
1030  end if
1031 
1032  '' byref arg? check if a temp param isn't needed
1033  if( symbGetParamMode( param ) = FB_PARAMMODE_BYREF ) then
1034  hCheckByrefParam( param, n )
1035  '' it's an implicit pointer
1036  end if
1037 
1038  function = TRUE
1039 
1040 end function
1041 
1042 function hCreateOptArg( byval param as FBSYMBOL ptr ) as ASTNODE ptr
1043  dim as ASTNODE ptr tree = symbGetParamOptExpr( param )
1044 
1045  if( tree = NULL ) then
1046  return NULL
1047  end if
1048 
1049  '' make a clone
1050  '' Note: Cannot assume TYPEINI here, because of RTL functions with
1051  '' other initializer expressions
1052  if( astIsTYPEINI( tree ) ) then
1053  tree = astTypeIniClone( tree )
1054 
1055  '' Try to remove the TYPEINI if it was just used as a wrapper
1056  '' to handle temp vars
1057  tree = astTypeIniTryRemove( tree )
1058  else
1059  tree = astCloneTree( tree )
1060  end if
1061 
1062  function = tree
1063 end function
1064 
1065 '':::::
1066 function astNewARG _
1067  ( _
1068  byval parent as ASTNODE ptr, _
1069  byval arg as ASTNODE ptr, _
1070  byval dtype as integer, _
1071  byval mode as integer = INVALID _
1072  ) as ASTNODE ptr
1073 
1074  dim as ASTNODE ptr n = any
1075  dim as FBSYMBOL ptr sym = any, param = any
1076 
1077  sym = parent->sym
1078 
1079  if( parent->call.args >= sym->proc.params ) then
1080  param = symbGetProcTailParam( sym )
1081  else
1082  param = parent->call.currarg
1083  end if
1084 
1085  '' optional/default?
1086  if( arg = NULL ) then
1087  arg = hCreateOptArg( param )
1088  end if
1089 
1090  if( dtype = FB_DATATYPE_INVALID ) then
1091  dtype = astGetFullType( arg )
1092  end if
1093 
1094  '' Complain about const arg passed to non-const non-byval param,
1095  '' unless it's a RTL function or the instance arg of a ctor/dtor call.
1096  '' (ctors/dtors should be able to operate even on const objects, because
1097  '' a ctor that can't initialize the object would be useless, and after
1098  '' dtors run the object is dead anyways, so modifications made by the
1099  '' dtor don't matter)
1100  if( ((symbGetIsRTL( sym ) = FALSE) or symbGetIsRTLConst( param )) and _
1101  ((not symbIsParamInstance( param )) or _
1102  ((not symbIsConstructor( sym )) and (not symbIsDestructor( sym )))) ) then
1103  if( symbCheckConstAssign( symbGetFullType( param ), dtype, param->subtype, arg->subtype, symbGetParamMode( param ) ) = FALSE ) then
1104  if( symbIsParamInstance( param ) ) then
1105  errReportParam( parent->sym, 0, NULL, FB_ERRMSG_CONSTUDTTONONCONSTMETHOD )
1106  else
1107  errReportParam( parent->sym, parent->call.args+1, NULL, FB_ERRMSG_ILLEGALASSIGNMENT )
1108  end if
1109  exit function
1110  end if
1111  end if
1112 
1113  '' alloc new node
1114  n = astNewNode( AST_NODECLASS_ARG, FB_DATATYPE_INVALID )
1115  function = n
1116 
1117  n->sym = param
1118  n->l = arg
1119  n->arg.mode = mode
1120  n->arg.lgt = 0
1121 
1122  '' Add ARGs to the CALL in the order they'll be pushed
1123  if( symbGetProcMode( sym ) = FB_FUNCMODE_PASCAL ) then
1124  '' Pascal, push as-is, this ARG is added to the end of the list
1125  if( parent->r = NULL ) then
1126  parent->r = n
1127  else
1128  parent->call.argtail->r = n
1129  end if
1130  parent->call.argtail = n
1131  n->r = NULL
1132  else
1133  '' Non-pascal, push in reverse order,
1134  '' this ARG is added to the front of the list
1135  if( parent->r = NULL ) then
1136  parent->call.argtail = n
1137  end if
1138  n->r = parent->r
1139  parent->r = n
1140  end if
1141 
1142  errPushParamLocation( parent->sym, -1, parent->call.args+1, NULL )
1143 
1144  ''
1145  if( hCheckParam( parent, param, n ) = FALSE ) then
1147  return NULL
1148  end if
1149 
1151 
1152  ''
1153  parent->call.args += 1
1154 
1155  if( parent->call.args < sym->proc.params ) then
1156  parent->call.currarg = symbGetParamNext( parent->call.currarg )
1157  end if
1158 
1159 end function
1160 
1162  ( _
1163  byval parent as ASTNODE ptr, _
1164  byval expr as ASTNODE ptr, _
1165  byval mode as integer _
1166  )
1167 
1168  dim as FBSYMBOL ptr sym = any, param = any
1169  dim as ASTNODE ptr n = any
1170 
1171  assert( astIsCALL( parent ) )
1172  sym = parent->sym
1173 
1174  '' For PASCAL procs, THIS will be the first ARG;
1175  '' for others, it will be the last
1176  if( symbGetProcMode( sym ) = FB_FUNCMODE_PASCAL ) then
1177  n = parent->r
1178  else
1179  n = parent->call.argtail
1180  end if
1181 
1182  param = symbGetProcHeadParam( sym )
1183  assert( symbIsParamInstance( param ) )
1184 
1185  '' Delete the old argument expression
1186  astDelTree( n->l )
1187 
1188  assert( n->sym = param )
1189  n->l = expr
1190  n->arg.mode = mode
1191  n->arg.lgt = 0
1192 
1193  hCheckParam( parent, param, n )
1194 end sub
1195