FreeBASIC  0.91.0
symb-struct.bas
Go to the documentation of this file.
1 '' symbol table module for user defined types (structures and unions)
2 ''
3 '' chng: sep/2004 written [v1ctor]
4 '' jan/2005 updated to use real linked-lists [v1ctor]
5 
6 
7 #include once "fb.bi"
8 #include once "fbint.bi"
9 #include once "parser.bi"
10 #include once "hash.bi"
11 #include once "list.bi"
12 #include once "ir.bi"
13 
14 ''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
15 '' add
16 ''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
17 
18 function symbStructBegin _
19  ( _
20  byval symtb as FBSYMBOLTB ptr, _
21  byval parent as FBSYMBOL ptr, _
22  byval id as const zstring ptr, _
23  byval id_alias as const zstring ptr, _
24  byval isunion as integer, _
25  byval align as integer, _
26  byval base_ as FBSYMBOL ptr, _
27  byval attrib as integer _
28  ) as FBSYMBOL ptr
29 
30  dim as FBSYMBOL ptr s = any
31 
32  function = NULL
33 
34  '' no explict alias given?
35  if( id_alias = NULL ) then
36  '' only preserve a case-sensitive version if in BASIC mangling
37  if( parser.mangling <> FB_MANGLING_BASIC ) then
38  id_alias = id
39  end if
40  end if
41 
42  s = symbNewSymbol( FB_SYMBOPT_DOHASH, NULL, symtb, NULL, _
43  FB_SYMBCLASS_STRUCT, id, id_alias, _
44  FB_DATATYPE_STRUCT, NULL, attrib )
45  if( s = NULL ) then
46  exit function
47  end if
48 
49  s->udt.options = 0
50  if( isunion ) then
51  symbSetUDTIsUnion( s )
52  end if
53 
54  symbSymbTbInit( s->udt.ns.symtb, s )
55 
56  '' not anon? create a new hash tb
57  if( parent = NULL ) then
58  symbHashTbInit( s->udt.ns.hashtb, s, FB_INITFIELDNODES )
59  '' anonymous, use the parent's hash tb..
60  else
61  s->udt.anonparent = parent
62  symbSetUDTIsAnon( s )
63  end if
64 
65  '' unused (while mixins aren't supported)
66  s->udt.ns.ext = NULL
67 
68  ''
69  s->ofs = 0
70 
71  '' Assume FIELD = 1 under -lang qb, because QB didn't do any alignment
72  if( fbLangIsSet( FB_LANG_QB ) ) then
73  if( align = 0 ) then
74  align = 1
75  end if
76  end if
77 
78  s->udt.align = align
79  s->udt.natalign = 1
80  s->udt.bitpos = 0
81  s->udt.unpadlgt = 0
82 
83  s->udt.dbg.typenum = INVALID
84 
85  s->udt.ext = NULL
86 
87  '' extending another UDT?
88  if( base_ <> NULL ) then
89  static as FBARRAYDIM dTB(0 to 0)
90 
91  s->udt.base = symbAddField( s, "$base", 0, dTB(), FB_DATATYPE_STRUCT, base_, 0, 0 )
92 
93  symbSetIsUnique( s )
94  symbNestBegin( s, FALSE )
95  symbNamespaceImportEx( base_, s )
96 
97  if( symbGetHasRTTI( base_ ) ) then
98  symbSetHasRTTI( s )
99 
100  '' inherit the vtable elements and abstracts counts
101  assert( base_->udt.ext->vtableelements >= 2 )
102  symbUdtAllocExt( s )
103  s->udt.ext->vtableelements = base_->udt.ext->vtableelements
104  s->udt.ext->abstractcount = base_->udt.ext->abstractcount
105  end if
106  else
107  s->udt.base = NULL
108  end if
109 
110  function = s
111 end function
112 
113 function typeCalcNaturalAlign _
114  ( _
115  byval dtype as integer, _
116  byval subtype as FBSYMBOL ptr _
117  ) as integer
118 
119  dim as integer align = any
120 
121  select case as const( typeGet( dtype ) )
122  '' UDT? its natural alignment depends on the largest field
123  case FB_DATATYPE_STRUCT
124  align = subtype->udt.natalign
125 
126  '' var-len string: largest field is the pointer at the front
127  case FB_DATATYPE_STRING
128  align = env.pointersize
129 
130  case else
131  '' Anything else (including zstring/wstring/fixlen strings)
132  '' use the base type's size (e.g. character size of strings)
133  align = typeGetSize( dtype )
134  end select
135 
136  if( fbCpuTypeIs64bit( ) = FALSE ) then
137  '' LONGINT/DOUBLE are 4-byte aligned on 32bit, except on Win32
138  '' (i.e. we don't have anything 8-byte aligned in this case)
139  if( align = 8 ) then
140  if( env.clopt.target <> FB_COMPTARGET_WIN32 ) then
141  align = 4
142  end if
143  end if
144  end if
145 
146  assert( (align >= 1) and (align <= 8) )
147 
148  function = align
149 end function
150 
151 function hCalcPadding _
152  ( _
153  byval ofs as longint, _
154  byval align as integer, _
155  byval dtype as integer, _
156  byval subtype as FBSYMBOL ptr _
157  ) as integer
158 
159  dim as integer natalign = any
160 
161  natalign = typeCalcNaturalAlign( dtype, subtype )
162 
163  '' default?
164  if( align = 0 ) then
165  assert( fbLangIsSet( FB_LANG_QB ) = FALSE )
166  align = natalign
167  '' packed..
168  else
169  '' Field is ok with smaller alignment than what's given for FIELD=N?
170  '' Then the field's alignment takes precedence, i.e. FIELD=N can
171  '' only decrease the alignment but not increase it.
172  if( align > natalign ) then
173  align = natalign
174  end if
175  end if
176 
177  '' Calculate the padding bytes needed to align the current offset,
178  '' so that offset mod align = 0.
179  function = (align - (ofs and (align - 1))) and (align - 1)
180 end function
181 
182 function hCheckUDTSize _
183  ( _
184  byval udtlen as ulongint, _
185  byval fieldlen as ulongint, _
186  byval fieldpad as uinteger _
187  ) as integer
188 
189  dim as ulongint n = any
190 
191  n = udtlen
192  n += fieldlen
193  n += fieldpad
194 
195  if( n > &h7FFFFFFFull ) then
196  function = FALSE
197  errReport( FB_ERRMSG_UDTTOOBIG )
198  else
199  function = TRUE
200  end if
201 end function
202 
203 '':::::
204 function symbCheckBitField _
205  ( _
206  byval udt as FBSYMBOL ptr, _
207  byval dtype as integer, _
208  byval lgt as longint, _
209  byval bits as integer _
210  ) as integer
211 
212  '' <= 0 or > sizeof(type)?
213  if( (bits <= 0) or (bits > lgt*8) ) then
214  return FALSE
215  end if
216 
217  '' not an integer type?
218  select case as const typeGet( dtype )
219  case FB_DATATYPE_BYTE, FB_DATATYPE_UBYTE, FB_DATATYPE_SHORT, FB_DATATYPE_USHORT, _
220  FB_DATATYPE_INTEGER, FB_DATATYPE_UINT, FB_DATATYPE_LONG, FB_DATATYPE_ULONG
221 
222  return TRUE
223 
224  case else
225  return FALSE
226  end select
227 
228 end function
229 
230 function symbAddBitField _
231  ( _
232  byval bitpos as integer, _
233  byval bits as integer, _
234  byval dtype as integer, _
235  byval lgt as longint _
236  ) as FBSYMBOL ptr
237 
238  dim as FBSYMBOL ptr sym = any
239 
240  '' table must be the global one, if the UDT is been defined
241  '' at main(), it will be deleted before some private function
242  '' accessing the bitfield
243 
244  sym = symbNewSymbol( FB_SYMBOPT_NONE, NULL, NULL, NULL, _
245  FB_SYMBCLASS_BITFIELD, NULL, NULL, dtype, NULL )
246  if( sym = NULL ) then
247  return NULL
248  end if
249 
250  sym->bitfld.bitpos = bitpos
251  sym->bitfld.bits = bits
252  sym->lgt = lgt
253 
254  function = sym
255 end function
256 
257 '':::::
258 function symbAddField _
259  ( _
260  byval parent as FBSYMBOL ptr, _
261  byval id as zstring ptr, _
262  byval dimensions as integer, _
263  dTB() as FBARRAYDIM, _
264  byval dtype as integer, _
265  byval subtype as FBSYMBOL ptr, _
266  byval lgt as longint, _
267  byval bits as integer _
268  ) as FBSYMBOL ptr static
269 
270  dim as FBSYMBOL ptr sym = any, tail = any, base_parent = any, prevbitfield = any
271  dim as integer pad = any, updateudt = any, elen = any
272  dim as FBHASHTB ptr hashtb
273 
274  function = NULL
275 
276  '' calc length if it wasn't given
277  if( lgt <= 0 ) then
278  lgt = symbCalcLen( dtype, subtype )
279  end if
280 
281  '' check if the parent ofs must be updated
282  updateudt = TRUE
283  if( bits > 0 ) then
284  '' last field was a bitfield too? try to merge..
285  if( parent->udt.bitpos > 0 ) then
286  '' Find the last field (skipping over methods etc.)
287  tail = parent->udt.ns.symtb.tail
288  while( symbIsField( tail ) = FALSE )
289  tail = tail->prev
290  wend
291 
292  assert( symbGetType( tail ) = FB_DATATYPE_BITFIELD )
293  prevbitfield = tail->subtype
294  assert( symbIsBitfield( prevbitfield ) )
295 
296  '' Too many bits to fit into previous bitfield container field?
297  if( parent->udt.bitpos + bits > prevbitfield->lgt*8 ) then
298  '' Start new container field, this bitfield will be at bitpos 0 in it
299  parent->udt.bitpos = 0
300  else
301  '' The previous container field still has enough
302  '' room to hold this new bitfield.
303 
304  '' if it fits but len is different, make it the same
305  '' TODO: is this "right"? shouldn't the different
306  '' type trigger a new container field to be used?
307  '' look what gcc does, with/without -mms-bitfields
308  '' This for now allows merging bitfields if they
309  '' have a different length, but maybe then this
310  '' check shouldn't just be done for different lengths,
311  '' but always if the dtypes are different?
312  if( lgt <> prevbitfield->lgt ) then
313  dtype = symbGetType( prevbitfield )
314  lgt = prevbitfield->lgt
315  end if
316  end if
317  end if
318 
319  '' don't update if there are enough bits left
320  if( parent->udt.bitpos <> 0 ) then
321  updateudt = FALSE
322  end if
323  else
324  '' Normal fields are not merged into bitfield containers,
325  '' so the bitfield merging is interrupted here.
326  parent->udt.bitpos = 0
327  end if
328 
329  ''
330  if( updateudt ) then
331  pad = hCalcPadding( parent->ofs, parent->udt.align, dtype, subtype )
332  if( pad > 0 ) then
333 
334  '' bitfield?
335  if( bits > 0 ) then
336  '' not M$-way?
337  if( env.clopt.msbitfields = FALSE ) then
338  '' follow the GCC ABI..
339  if( bits <= pad * 8 ) then
340  lgt = pad
341  pad = 0
342 
343  '' remap type
344  select case lgt
345  case 1
346  if( typeIsSigned( dtype ) ) then
347  dtype = FB_DATATYPE_BYTE
348  else
349  dtype = FB_DATATYPE_UBYTE
350  end if
351  case 2
352  if( typeIsSigned( dtype ) ) then
353  dtype = FB_DATATYPE_SHORT
354  else
355  dtype = FB_DATATYPE_USHORT
356  end if
357 
358  '' padding won't be >= sizeof(int) because only
359  '' integers can be used as bitfields
360  end select
361 
362  end if
363 
364  end if
365  end if
366  end if
367 
368  '' Check whether adding this field would make the UDT be too big
369  if( hCheckUDTSize( parent->ofs, lgt, pad ) ) then
370  parent->ofs += pad
371  else
372  '' error recovery: don't add this field
373  updateudt = FALSE
374  end if
375 
376  '' update largest field len
377  elen = typeCalcNaturalAlign( dtype, subtype )
378  '' larger?
379  if( elen > parent->udt.natalign ) then
380  parent->udt.natalign = elen
381  end if
382  end if
383 
384  '' bitfield?
385  if( bits > 0 ) then
386  subtype = symbAddBitField( parent->udt.bitpos, bits, dtype, lgt )
387  dtype = FB_DATATYPE_BITFIELD
388  end if
389 
390  '' use the base parent hashtb if it's an anonymous type
391  base_parent = parent
392  do while( symbGetUDTIsAnon( base_parent ) )
393  base_parent = symbGetUDTAnonParent( base_parent )
394  loop
395 
396  hashtb = @symbGetUDTHashTb( base_parent )
397 
398  ''
399  sym = symbNewSymbol( FB_SYMBOPT_DOHASH, _
400  NULL, _
401  @symbGetUDTSymbTb( parent ), hashtb, _
402  FB_SYMBCLASS_FIELD, _
403  id, NULL, _
404  dtype, subtype, _
405  iif( symbIsLocal( parent ), _
406  FB_SYMBATTRIB_LOCAL, _
407  FB_SYMBATTRIB_NONE ) )
408  if( sym = NULL ) then
409  exit function
410  end if
411 
412  sym->lgt = lgt
413 
414  if( updateudt or symbGetUDTIsUnion( parent ) ) then
415  sym->ofs = parent->ofs
416  else
417  sym->ofs = parent->ofs - lgt
418  end if
419 
420  ''
421  sym->var_.initree = NULL
422 
423  '' array fields
424  sym->var_.array.desc = NULL
425  sym->var_.array.dif = symbCalcArrayDiff( dimensions, dTB(), lgt )
426  sym->var_.array.dimhead = NULL
427  sym->var_.array.dimtail = NULL
428  sym->var_.array.has_ellipsis = FALSE
429 
430  symbSetArrayDimensions( sym, dimensions )
431  if( dimensions > 0 ) then
432  for i as integer = 0 to dimensions-1
433  symbAddArrayDim( sym, dTB(i).lower, dTB(i).upper )
434  next
435  end if
436 
437  sym->var_.array.elms = symbCalcArrayElements( sym )
438 
439  '' multiple len by all array elements (if any)
440  lgt *= sym->var_.array.elms
441 
442  select case as const typeGet( dtype )
443  '' var-len string fields? must add a ctor, copyctor and dtor
444  case FB_DATATYPE_STRING
445  '' not allowed inside unions or anonymous nested structs/unions
446  if( symbGetUDTIsUnionOrAnon( parent ) ) then
447  errReport( FB_ERRMSG_VARLENSTRINGINUNION )
448  else
449  symbSetUDTHasCtorField( parent )
450  symbSetUDTHasDtorField( parent )
451  symbSetUDTHasPtrField( parent )
452  end if
453 
454  '' struct with a ctor or dtor? must add a ctor or dtor too
455  case FB_DATATYPE_STRUCT
456  '' Let the FB_UDTOPT_HASPTRFIELD flag propagate up to the
457  '' parent if this field has it.
458  if( symbGetUDTHasPtrField( subtype ) ) then
459  symbSetUDTHasPtrField( base_parent )
460  end if
461 
462  if( symbGetCompCtorHead( subtype ) ) then
463  '' not allowed inside unions or anonymous nested structs/unions
464  if( symbGetUDTIsUnionOrAnon( parent ) ) then
465  errReport( FB_ERRMSG_CTORINUNION )
466  else
467  symbSetUDTHasCtorField( parent )
468  end if
469  end if
470 
471  if( symbGetCompDtor( subtype ) ) then
472  '' not allowed inside unions or anonymous nested structs/unions
473  if( symbGetUDTIsUnionOrAnon( parent ) ) then
474  errReport( FB_ERRMSG_DTORINUNION )
475  else
476  symbSetUDTHasDtorField( parent )
477  end if
478  end if
479 
480  end select
481 
482  '' check pointers
483  if( typeIsPtr( dtype ) ) then
484  symbSetUDTHasPtrField( base_parent )
485  end if
486 
487  '' struct?
488  if( symbGetUDTIsUnion( parent ) = FALSE ) then
489  if( updateudt ) then
490  parent->ofs += lgt
491  parent->lgt = parent->ofs
492  end if
493 
494  '' update the bit position, wrapping around
495  if( bits > 0 ) then
496  parent->udt.bitpos += bits
497  parent->udt.bitpos and= (typeGetBits( dtype ) - 1)
498  end if
499 
500  '' union..
501  else
502  symbSetIsUnionField( sym )
503 
504  '' always update, been it a bitfield or not
505  parent->ofs = 0
506  if( lgt > parent->lgt ) then
507  parent->lgt = lgt
508  end if
509 
510  '' bit position doesn't change in a union
511  end if
512 
513  function = sym
514 
515  sym->parent = parent
516 
517 end function
518 
519 sub symbInsertInnerUDT _
520  ( _
521  byval parent as FBSYMBOL ptr, _
522  byval inner as FBSYMBOL ptr _
523  )
524 
525  dim as FBSYMBOL ptr fld = any
526  dim as FBSYMBOLTB ptr symtb = any
527  dim as integer pad = any
528 
529  if( symbGetUDTIsUnion( parent ) = FALSE ) then
530  '' calc padding (should be aligned like if an UDT field was being added)
531  pad = hCalcPadding( parent->ofs, parent->udt.align, FB_DATATYPE_STRUCT, inner )
532  if( hCheckUDTSize( parent->ofs, 0, pad ) ) then
533  parent->ofs += pad
534  end if
535  end if
536 
537  '' move the nodes from inner to parent
538  fld = inner->udt.ns.symtb.head
539 
540  '' unless it's a fake struct
541  if( fld = NULL ) then
542  exit sub
543  end if
544 
545  fld->prev = parent->udt.ns.symtb.tail
546  if( parent->udt.ns.symtb.tail = NULL ) then
547  parent->udt.ns.symtb.head = fld
548  else
549  parent->udt.ns.symtb.tail->next = fld
550  end if
551 
552  symtb = @parent->udt.ns.symtb
553 
554  if( symbGetUDTIsUnion( parent ) ) then
555  '' link to parent
556  do while( fld <> NULL )
557  fld->symtb = symtb
558  symbSetIsUnionField( fld )
559  '' next
560  fld = fld->next
561  loop
562  else
563  '' link to parent
564  do while( fld <> NULL )
565  fld->symtb = symtb
566  '' update the offset
567  fld->ofs += parent->ofs
568  '' next
569  fld = fld->next
570  loop
571  end if
572 
573  parent->udt.ns.symtb.tail = inner->udt.ns.symtb.tail
574 
575  '' struct? update ofs + len
576  if( symbGetUDTIsUnion( parent ) = FALSE ) then
577  parent->ofs += inner->lgt
578  parent->lgt = parent->ofs
579  '' union.. update len, if bigger
580  else
581  parent->ofs = 0
582  if( inner->lgt > parent->lgt ) then
583  parent->lgt = inner->lgt
584  end if
585  end if
586 
587  '' update the natural alignment
588  if( inner->udt.natalign > parent->udt.natalign ) then
589  parent->udt.natalign = inner->udt.natalign
590  end if
591 
592  '' reset bitfield
593  parent->udt.bitpos = 0
594 
595  '' remove from inner udt list
596  inner->udt.ns.symtb.head = NULL
597  inner->udt.ns.symtb.tail = NULL
598 
599  inner->parent = parent
600 
601 end sub
602 
603 function hGetReturnType( byval sym as FBSYMBOL ptr ) as integer
604  dim as FBSYMBOL ptr fld = any
605  dim as integer res = any, unpadlen = any
606  dim as longint unpadlen64 = any
607 
608  '' UDT has a dtor, copy-ctor or virtual methods?
609  if( symbCompIsTrivial( sym ) = FALSE ) then
610  '' It's always returned through a hidden param on stack
611  return typeAddrOf( FB_DATATYPE_STRUCT )
612  end if
613 
614  '' On Linux & co structures are never returned in registers
615  if( (env.target.options and FB_TARGETOPT_RETURNINREGS) = 0 ) then
616  return typeAddrOf( FB_DATATYPE_STRUCT )
617  end if
618 
619  '' C backend? Leave the type as-is instead of lowering to the real
620  '' "return-in-regs" type, this means we can generate nicer C code,
621  '' since UDT vars also use the original type, they can be used with
622  '' RETURN in C without needing a cast.
623  if( env.clopt.backend = FB_BACKEND_GCC ) then
624  return FB_DATATYPE_STRUCT
625  end if
626 
627  res = FB_DATATYPE_VOID
628 
629  '' Check whether the structure is small enough to be returned in
630  '' registers, and if so, select the proper dtype. For this, the
631  '' un-padded UDT length should be checked so we can handle the cases
632  '' where length=1/2/3.
633  unpadlen64 = symbGetUDTUnpadLen( sym )
634 
635  '' Check for longint -> integer overflow, otherwise that could happen
636  '' to the SELECT's temp var below
637  unpadlen = unpadlen64
638  if( unpadlen <> unpadlen64 ) then
639  '' very big structure (> 2GiB), no way to return in registers
640  return FB_DATATYPE_STRUCT
641  end if
642 
643  select case as const( unpadlen )
644  case 1
645  res = FB_DATATYPE_BYTE
646 
647  case 2
648  res = FB_DATATYPE_SHORT
649 
650  case 3
651  '' return as int only if first is a short
652  fld = symbUdtGetFirstField( sym )
653  if( fld->lgt = 2 ) then
654  '' and if the struct is not packed
655  if( sym->lgt >= 4 ) then
656  res = FB_DATATYPE_INTEGER
657  end if
658  end if
659 
660  case 4
661  '' return in ST(0) if there's only one element and it's a SINGLE
662  do
663  fld = symbUdtGetFirstField( sym )
664 
665  '' second field?
666  if( symbUdtGetNextField( fld ) ) then
667  exit do
668  end if
669 
670  if( typeGetDtAndPtrOnly( fld->typ ) = FB_DATATYPE_SINGLE ) then
671  res = FB_DATATYPE_SINGLE
672  end if
673 
674  if( typeGetDtAndPtrOnly( fld->typ ) <> FB_DATATYPE_STRUCT ) then
675  exit do
676  end if
677 
678  sym = fld->subtype
679  loop
680 
681  if( res = FB_DATATYPE_VOID ) then
682  res = FB_DATATYPE_INTEGER
683  end if
684 
685  case 5, 6, 7
686  '' return as longint only if first is a int
687  fld = symbUdtGetFirstField( sym )
688  if( fld->lgt = 4 ) then
689  '' and if the struct is not packed
690  if( sym->lgt >= 8 ) then
691  res = FB_DATATYPE_LONGINT
692  end if
693  end if
694 
695  case 8
696  '' return in ST(0) if there's only one element and it's a DOUBLE
697  do
698  fld = symbUdtGetFirstField( sym )
699 
700  '' second field?
701  if( symbUdtGetNextField( fld ) ) then
702  exit do
703  end if
704 
705  if( typeGetDtAndPtrOnly( fld->typ ) = FB_DATATYPE_DOUBLE ) then
706  res = FB_DATATYPE_DOUBLE
707  end if
708 
709  if( typeGetDtAndPtrOnly( fld->typ ) <> FB_DATATYPE_STRUCT ) then
710  exit do
711  end if
712 
713  sym = fld->subtype
714  loop
715 
716  if( res = FB_DATATYPE_VOID ) then
717  res = FB_DATATYPE_LONGINT
718  end if
719 
720  end select
721 
722  '' Nothing matched?
723  if( res = FB_DATATYPE_VOID ) then
724  '' Then it's returned through a hidden param on stack
725  res = typeAddrOf( FB_DATATYPE_STRUCT )
726  end if
727 
728  function = res
729 end function
730 
731 sub symbStructEnd _
732  ( _
733  byval sym as FBSYMBOL ptr, _
734  byval isnested as integer _
735  )
736 
737  dim as integer pad = any
738 
739  '' end nesting?
740  if( isnested ) then
741  symbNestEnd( FALSE )
742  end if
743 
744  '' save length without the tail padding added below
745  sym->udt.unpadlgt = sym->lgt
746 
747  '' Add tail padding bytes, i.e. round up the structure size to match
748  '' the alignment of the largest natural field.
749  pad = hCalcPadding( sym->lgt, sym->udt.align, FB_DATATYPE_STRUCT, sym )
750  if( hCheckUDTSize( sym->lgt, 0, pad ) ) then
751  sym->lgt += pad
752  end if
753 
754  '' set the real data type used to return this struct from procs
755  sym->udt.retdtype = hGetReturnType( sym )
756 
757  '' Declare & add any implicit members
759 
760  '' check for forward references
761  if( symb.fwdrefcnt > 0 ) then
762  symbCheckFwdRef( sym )
763  end if
764 
765 end sub
766 
767 function symbCloneStruct( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr
768  static as FBARRAYDIM dTB(0)
769  dim as FBSYMBOL ptr clone = any, fld = any
770 
771  '' assuming only simple structs will be cloned (ie: the ones
772  '' created by symbAddArrayDesc())
773 
774  clone = symbStructBegin( NULL, NULL, symbUniqueId( ), NULL, _
775  symbGetUDTIsUnion( sym ), _
776  sym->udt.align, NULL, 0 )
777 
778  fld = sym->udt.ns.symtb.head
779  while( fld )
780  symbAddField( clone, symbGetName( fld ), 0, dTB(), _
781  symbGetType( fld ), symbGetSubType( fld ), fld->lgt, 0 )
782  fld = fld->next
783  wend
784 
785  symbStructEnd( clone )
786 
787  function = clone
788 end function
789 
790 ''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
791 '' del
792 ''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
793 
794 sub symbDelField( byval s as FBSYMBOL ptr )
795  if( symbGetArrayDimensions( s ) > 0 ) then
796  symbDelVarDims( s )
797  end if
798  '' Note: astEnd() will already free the initree
799  symbFreeSymbol( s )
800 end sub
801 
802 sub symbDelStruct( byval s as FBSYMBOL ptr )
803  symbDelNamespaceMembers( s, (not symbGetUDTIsAnon( s )) )
804 
805  if( s->udt.ext ) then
806  deallocate( s->udt.ext )
807  s->udt.ext = NULL
808  end if
809 
810  symbFreeSymbol( s )
811 end sub
812 
813 ''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
814 '' misc
815 ''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
816 
817 function hSkipToField( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr
818  '' Skip over anything that isn't a field,
819  '' e.g. PROCs (methods) or NSIMPORTs (in derived UDTs)
820  while( sym )
821  if( symbIsField( sym ) ) then
822  exit while
823  end if
824  sym = sym->next
825  wend
826  function = sym
827 end function
828 
829 function symbUdtGetFirstField( byval parent as FBSYMBOL ptr ) as FBSYMBOL ptr
830  '' Get first member that is a field
831  function = hSkipToField( symbGetUDTSymbTbHead( parent ) )
832 end function
833 
834 function symbUdtGetNextField( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr
835  function = hSkipToField( sym->next )
836 end function
837 
838 function hFindCommonParent _
839  ( _
840  byval a as FBSYMBOL ptr, _
841  byval b as FBSYMBOL ptr _
842  ) as FBSYMBOL ptr
843 
844  dim as FBSYMBOL ptr originalb = any
845 
846  originalb = b
847 
848  '' For a and each parent of a,
849  '' check whether it matches b or one of b's parents.
850  while( a )
851  b = originalb
852  while( b )
853  if( a = b ) then
854  return a
855  end if
856  b = b->parent
857  wend
858  a = a->parent
859  wend
860 
861  function = NULL
862 end function
863 
864 function symbUdtGetNextInitableField( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr
865  dim as FBSYMBOL ptr original = any, parent = any
866 
867  ''
868  '' Move to the next field that should be initialized.
869  '' Unions are special cases: only their first field can be initialized,
870  '' so if <sym> is from a union, the remaining fields in the union
871  '' must be skipped.
872  ''
873  '' Example:
874  ''
875  '' type
876  '' a as integer '' reached
877  '' union
878  '' b as integer '' reached
879  '' c as integer '' skipped
880  '' end union
881  '' union
882  '' d as integer '' reached
883  '' end union
884  '' e as integer '' reached
885  '' union
886  '' type
887  '' f as integer '' reached
888  '' g as integer '' reached
889  '' end type
890  '' h as integer '' skipped
891  '' type
892  '' i as integer '' skipped
893  '' j as integer '' skipped
894  '' end type
895  '' k as integer '' skipped
896  '' end union
897  '' l as integer '' reached
898  '' end type
899  ''
900 
901  original = sym
902 
903  do
904  '' Move to next field, if any
905  sym = symbUdtGetNextField( sym )
906  if( sym = NULL ) then
907  exit do
908  end if
909 
910  '' If the greatest common parent of the reached field and the
911  '' original field is a union (not a struct), then the reached
912  '' field must be skipped.
913  parent = hFindCommonParent( original, sym )
914  if( parent = NULL ) then
915  exit do
916  end if
917  if( symbGetUDTIsUnion( parent ) = FALSE ) then
918  exit do
919  end if
920  loop
921 
922  function = sym
923 end function
924 
925 '':::::
926 function symbGetUDTBaseLevel _
927  ( _
928  byval s as FBSYMBOL ptr, _
929  byval baseSym as FBSYMBOL ptr _
930  ) as integer
931 
932  if( s = NULL or baseSym = NULL ) then
933  return 0
934  end if
935 
936  assert( symbIsStruct( s ) )
937  assert( symbIsStruct( baseSym ) )
938 
939  var level = 1
940  do until( s->udt.base = NULL )
941  if( s->udt.base->subtype = baseSym ) then
942  return level
943  End If
944 
945  s = s->udt.base->subtype
946  level += 1
947  Loop
948 
949  return 0
950 
951 End Function
952