Merge pull request #820 from brendanzagaeski/master
[mono.git] / mcs / mcs / flowanalysis.cs
1 //
2 // flowanalyis.cs: The control flow analysis code
3 //
4 // Authors:
5 //   Martin Baulig (martin@ximian.com)
6 //   Raja R Harinath (rharinath@novell.com)
7 //   Marek Safar (marek.safar@gmail.com)
8 //
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003-2008 Novell, Inc.
11 // Copyright 2011 Xamarin, Inc.
12 //
13
14 using System;
15 using System.Text;
16 using System.Collections.Generic;
17
18 namespace Mono.CSharp
19 {
20         // <summary>
21         //   This is used by the flow analysis code to keep track of the type of local variables.
22         //
23         //   The flow code uses a BitVector to keep track of whether a variable has been assigned
24         //   or not.  This is easy for fundamental types (int, char etc.) or reference types since
25         //   you can only assign the whole variable as such.
26         //
27         //   For structs, we also need to keep track of all its fields.  To do this, we allocate one
28         //   bit for the struct itself (it's used if you assign/access the whole struct) followed by
29         //   one bit for each of its fields.
30         //
31         //   This class computes this `layout' for each type.
32         // </summary>
33         public class TypeInfo
34         {
35                 // <summary>
36                 //   Total number of bits a variable of this type consumes in the flow vector.
37                 // </summary>
38                 public readonly int TotalLength;
39
40                 // <summary>
41                 //   Number of bits the simple fields of a variable of this type consume
42                 //   in the flow vector.
43                 // </summary>
44                 public readonly int Length;
45
46                 // <summary>
47                 //   This is only used by sub-structs.
48                 // </summary>
49                 public readonly int Offset;
50
51                 // <summary>
52                 //   If this is a struct.
53                 // </summary>
54                 public readonly bool IsStruct;
55
56                 // <summary>
57                 //   If this is a struct, all fields which are structs theirselves.
58                 // </summary>
59                 public TypeInfo[] SubStructInfo;
60
61                 readonly StructInfo struct_info;
62                 private static Dictionary<TypeSpec, TypeInfo> type_hash;
63
64                 static readonly TypeInfo simple_type = new TypeInfo (1);
65                 
66                 static TypeInfo ()
67                 {
68                         Reset ();
69                 }
70                 
71                 public static void Reset ()
72                 {
73                         type_hash = new Dictionary<TypeSpec, TypeInfo> ();
74                         StructInfo.field_type_hash = new Dictionary<TypeSpec, StructInfo> ();
75                 }
76
77                 TypeInfo (int totalLength)
78                 {
79                         this.TotalLength = totalLength;
80                 }
81                 
82                 TypeInfo (StructInfo struct_info, int offset)
83                 {
84                         this.struct_info = struct_info;
85                         this.Offset = offset;
86                         this.Length = struct_info.Length;
87                         this.TotalLength = struct_info.TotalLength;
88                         this.SubStructInfo = struct_info.StructFields;
89                         this.IsStruct = true;
90                 }
91                 
92                 public int GetFieldIndex (string name)
93                 {
94                         if (struct_info == null)
95                                 return 0;
96
97                         return struct_info [name];
98                 }
99
100                 public TypeInfo GetStructField (string name)
101                 {
102                         if (struct_info == null)
103                                 return null;
104
105                         return struct_info.GetStructField (name);
106                 }
107
108                 public static TypeInfo GetTypeInfo (TypeSpec type)
109                 {
110                         if (!type.IsStruct)
111                                 return simple_type;
112
113                         TypeInfo info;
114                         if (type_hash.TryGetValue (type, out info))
115                                 return info;
116
117                         var struct_info = StructInfo.GetStructInfo (type);
118                         if (struct_info != null) {
119                                 info = new TypeInfo (struct_info, 0);
120                         } else {
121                                 info = simple_type;
122                         }
123
124                         type_hash.Add (type, info);
125                         return info;
126                 }
127
128                 // <summary>
129                 //   A struct's constructor must always assign all fields.
130                 //   This method checks whether it actually does so.
131                 // </summary>
132                 public bool IsFullyInitialized (FlowAnalysisContext fc, VariableInfo vi, Location loc)
133                 {
134                         if (struct_info == null)
135                                 return true;
136
137                         bool ok = true;
138                         for (int i = 0; i < struct_info.Count; i++) {
139                                 var field = struct_info.Fields[i];
140
141                                 if (!fc.IsStructFieldDefinitelyAssigned (vi, field.Name)) {
142                                         if (field.MemberDefinition is Property.BackingField) {
143                                                 fc.Report.Error (843, loc,
144                                                         "An automatically implemented property `{0}' must be fully assigned before control leaves the constructor. Consider calling the default struct contructor from a constructor initializer",
145                                                         field.GetSignatureForError ());
146                                         } else {
147                                                 fc.Report.Error (171, loc,
148                                                         "Field `{0}' must be fully assigned before control leaves the constructor",
149                                                         field.GetSignatureForError ());
150                                         }
151                                         ok = false;
152                                 }
153                         }
154
155                         return ok;
156                 }
157
158                 public override string ToString ()
159                 {
160                         return String.Format ("TypeInfo ({0}:{1}:{2})",
161                                               Offset, Length, TotalLength);
162                 }
163
164                 class StructInfo
165                 {
166                         readonly List<FieldSpec> fields;
167                         public readonly TypeInfo[] StructFields;
168                         public readonly int Length;
169                         public readonly int TotalLength;
170
171                         public static Dictionary<TypeSpec, StructInfo> field_type_hash;
172                         private Dictionary<string, TypeInfo> struct_field_hash;
173                         private Dictionary<string, int> field_hash;
174
175                         bool InTransit;
176
177                         //
178                         // We only need one instance per type
179                         //
180                         StructInfo (TypeSpec type)
181                         {
182                                 field_type_hash.Add (type, this);
183
184                                 fields = MemberCache.GetAllFieldsForDefiniteAssignment (type);
185
186                                 struct_field_hash = new Dictionary<string, TypeInfo> ();
187                                 field_hash = new Dictionary<string, int> (fields.Count);
188
189                                 StructFields = new TypeInfo[fields.Count];
190                                 StructInfo[] sinfo = new StructInfo[fields.Count];
191
192                                 InTransit = true;
193
194                                 for (int i = 0; i < fields.Count; i++) {
195                                         var field = fields [i];
196
197                                         if (field.MemberType.IsStruct)
198                                                 sinfo [i] = GetStructInfo (field.MemberType);
199
200                                         if (sinfo [i] == null)
201                                                 field_hash.Add (field.Name, ++Length);
202                                         else if (sinfo [i].InTransit) {
203                                                 sinfo [i] = null;
204                                                 return;
205                                         }
206                                 }
207
208                                 InTransit = false;
209
210                                 TotalLength = Length + 1;
211                                 for (int i = 0; i < fields.Count; i++) {
212                                         var field = fields [i];
213
214                                         if (sinfo [i] == null)
215                                                 continue;
216
217                                         field_hash.Add (field.Name, TotalLength);
218
219                                         StructFields [i] = new TypeInfo (sinfo [i], TotalLength);
220                                         struct_field_hash.Add (field.Name, StructFields [i]);
221                                         TotalLength += sinfo [i].TotalLength;
222                                 }
223                         }
224
225                         public int Count {
226                                 get {
227                                         return fields.Count;
228                                 }
229                         }
230
231                         public List<FieldSpec> Fields {
232                                 get {
233                                         return fields;
234                                 }
235                         }
236
237                         public int this [string name] {
238                                 get {
239                                         int val;
240                                         if (!field_hash.TryGetValue (name, out val))
241                                                 return 0;
242
243                                         return val;
244                                 }
245                         }
246
247                         public TypeInfo GetStructField (string name)
248                         {
249                                 TypeInfo ti;
250                                 if (struct_field_hash.TryGetValue (name, out ti))
251                                         return ti;
252
253                                 return null;
254                         }
255
256                         public static StructInfo GetStructInfo (TypeSpec type)
257                         {
258                                 if (type.BuiltinType > 0)
259                                         return null;
260
261                                 StructInfo info;
262                                 if (field_type_hash.TryGetValue (type, out info))
263                                         return info;
264
265                                 return new StructInfo (type);
266                         }
267                 }
268         }
269
270         //
271         // This is used by definite assignment analysis code to store information about a local variable
272         // or parameter.  Depending on the variable's type, we need to allocate one or more elements
273         // in the BitVector - if it's a fundamental or reference type, we just need to know whether
274         // it has been assigned or not, but for structs, we need this information for each of its fields.
275         //
276         public class VariableInfo
277         {
278                 readonly string Name;
279
280                 readonly TypeInfo TypeInfo;
281
282                 // <summary>
283                 //   The bit offset of this variable in the flow vector.
284                 // </summary>
285                 readonly int Offset;
286
287                 // <summary>
288                 //   The number of bits this variable needs in the flow vector.
289                 //   The first bit always specifies whether the variable as such has been assigned while
290                 //   the remaining bits contain this information for each of a struct's fields.
291                 // </summary>
292                 readonly int Length;
293
294                 // <summary>
295                 //   If this is a parameter of local variable.
296                 // </summary>
297                 public bool IsParameter;
298
299                 VariableInfo[] sub_info;
300
301                 VariableInfo (string name, TypeSpec type, int offset)
302                 {
303                         this.Name = name;
304                         this.Offset = offset;
305                         this.TypeInfo = TypeInfo.GetTypeInfo (type);
306
307                         Length = TypeInfo.TotalLength;
308
309                         Initialize ();
310                 }
311
312                 VariableInfo (VariableInfo parent, TypeInfo type)
313                 {
314                         this.Name = parent.Name;
315                         this.TypeInfo = type;
316                         this.Offset = parent.Offset + type.Offset;
317                         this.Length = type.TotalLength;
318
319                         this.IsParameter = parent.IsParameter;
320
321                         Initialize ();
322                 }
323
324                 void Initialize ()
325                 {
326                         TypeInfo[] sub_fields = TypeInfo.SubStructInfo;
327                         if (sub_fields != null) {
328                                 sub_info = new VariableInfo [sub_fields.Length];
329                                 for (int i = 0; i < sub_fields.Length; i++) {
330                                         if (sub_fields [i] != null)
331                                                 sub_info [i] = new VariableInfo (this, sub_fields [i]);
332                                 }
333                         } else
334                                 sub_info = new VariableInfo [0];
335                 }
336
337                 public static VariableInfo Create (BlockContext bc, LocalVariable variable)
338                 {
339                         var info = new VariableInfo (variable.Name, variable.Type, bc.AssignmentInfoOffset);
340                         bc.AssignmentInfoOffset += info.Length;
341                         return info;
342                 }
343
344                 public static VariableInfo Create (BlockContext bc, Parameter parameter)
345                 {
346                         var info = new VariableInfo (parameter.Name, parameter.Type, bc.AssignmentInfoOffset) {
347                                 IsParameter = true
348                         };
349
350                         bc.AssignmentInfoOffset += info.Length;
351                         return info;
352                 }
353
354                 public bool IsAssigned (DefiniteAssignmentBitSet vector)
355                 {
356                         if (vector == null)
357                                 return true;
358
359                         if (vector [Offset])
360                                 return true;
361
362                         // Unless this is a struct
363                         if (!TypeInfo.IsStruct)
364                                 return false;
365
366                         //
367                         // Following case cannot be handled fully by SetStructFieldAssigned
368                         // because we may encounter following case
369                         // 
370                         // struct A { B b }
371                         // struct B { int value; }
372                         //
373                         // setting a.b.value is propagated only to B's vector and not upwards to possible parents
374                         //
375                         //
376                         // Each field must be assigned
377                         //
378                         for (int i = Offset + 1; i <= TypeInfo.Length + Offset; i++) {
379                                 if (!vector[i])
380                                         return false;
381                         }
382
383                         // Ok, now check all fields which are structs.
384                         for (int i = 0; i < sub_info.Length; i++) {
385                                 VariableInfo sinfo = sub_info[i];
386                                 if (sinfo == null)
387                                         continue;
388
389                                 if (!sinfo.IsAssigned (vector))
390                                         return false;
391                         }
392                         
393                         vector.Set (Offset);
394                         return true;
395                 }
396
397                 public bool IsEverAssigned { get; set; }
398
399                 public bool IsFullyInitialized (FlowAnalysisContext fc, Location loc)
400                 {
401                         return TypeInfo.IsFullyInitialized (fc, this, loc);
402                 }
403
404                 public bool IsStructFieldAssigned (DefiniteAssignmentBitSet vector, string field_name)
405                 {
406                         int field_idx = TypeInfo.GetFieldIndex (field_name);
407
408                         if (field_idx == 0)
409                                 return true;
410
411                         return vector [Offset + field_idx];
412                 }
413
414                 public void SetAssigned (DefiniteAssignmentBitSet vector, bool generatedAssignment)
415                 {
416                         if (Length == 1)
417                                 vector.Set (Offset);
418                         else
419                                 vector.Set (Offset, Length);
420
421                         if (!generatedAssignment)
422                                 IsEverAssigned = true;
423                 }
424
425                 public void SetStructFieldAssigned (DefiniteAssignmentBitSet vector, string field_name)
426                 {
427                         if (vector [Offset])
428                                 return;
429
430                         int field_idx = TypeInfo.GetFieldIndex (field_name);
431
432                         if (field_idx == 0)
433                                 return;
434
435                         var complex_field = TypeInfo.GetStructField (field_name);
436                         if (complex_field != null) {
437                                 vector.Set (Offset + complex_field.Offset, complex_field.TotalLength);
438                         } else {
439                                 vector.Set (Offset + field_idx);
440                         }
441
442                         IsEverAssigned = true;
443
444                         //
445                         // Each field must be assigned before setting master bit
446                         //
447                         for (int i = Offset + 1; i < TypeInfo.TotalLength + Offset; i++) {
448                                 if (!vector[i])
449                                         return;
450                         }
451
452                         //
453                         // Set master struct flag to assigned when all tested struct
454                         // fields have been assigned
455                         //
456                         vector.Set (Offset);
457                 }
458
459                 public VariableInfo GetStructFieldInfo (string fieldName)
460                 {
461                         TypeInfo type = TypeInfo.GetStructField (fieldName);
462
463                         if (type == null)
464                                 return null;
465
466                         return new VariableInfo (this, type);
467                 }
468
469                 public override string ToString ()
470                 {
471                         return String.Format ("Name={0} Offset={1} Length={2} {3})", Name, Offset, Length, TypeInfo);
472                 }
473         }
474
475         public struct Reachability
476         {
477                 readonly bool unreachable;
478
479                 Reachability (bool unreachable)
480                 {
481                         this.unreachable = unreachable;
482                 }
483
484                 public bool IsUnreachable {
485                         get {
486                                 return unreachable;
487                         }
488                 }
489
490                 public static Reachability CreateUnreachable ()
491                 {
492                         return new Reachability (true);
493                 }
494
495                 public static Reachability operator & (Reachability a, Reachability b)
496                 {
497                     return new Reachability (a.unreachable && b.unreachable);
498                 }
499
500                 public static Reachability operator | (Reachability a, Reachability b)
501                 {
502                         return new Reachability (a.unreachable | b.unreachable);
503                 }
504         }
505
506         //
507         // Special version of bit array. Many operations can be simplified because
508         // we are always dealing with arrays of same sizes
509         //
510         public class DefiniteAssignmentBitSet
511         {
512                 const uint copy_on_write_flag = 1u << 31;
513
514                 uint bits;
515
516                 // Used when bits overflows
517                 int[] large_bits;
518
519                 public static readonly DefiniteAssignmentBitSet Empty = new DefiniteAssignmentBitSet (0);
520
521                 public DefiniteAssignmentBitSet (int length)
522                 {
523                         if (length > 31)
524                                 large_bits = new int[(length + 31) / 32];
525                 }
526
527                 public DefiniteAssignmentBitSet (DefiniteAssignmentBitSet source)
528                 {
529                         if (source.large_bits != null) {
530                                 large_bits = source.large_bits;
531                                 bits = source.bits | copy_on_write_flag;
532                         } else {
533                                 bits = source.bits & ~copy_on_write_flag;
534                         }
535                 }
536
537                 public static DefiniteAssignmentBitSet operator & (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b)
538                 {
539                         if (AreEqual (a, b))
540                                 return a;
541
542                         DefiniteAssignmentBitSet res;
543                         if (a.large_bits == null) {
544                                 res = new DefiniteAssignmentBitSet (a);
545                                 res.bits &= (b.bits & ~copy_on_write_flag);
546                                 return res;
547                         }
548
549                         res = new DefiniteAssignmentBitSet (a);
550                         res.Clone ();
551                         var dest = res.large_bits;
552                         var src = b.large_bits;
553                         for (int i = 0; i < dest.Length; ++i) {
554                                 dest[i] &= src[i];
555                         }
556
557                         return res;
558                 }
559
560                 public static DefiniteAssignmentBitSet operator | (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b)
561                 {
562                         if (AreEqual (a, b))
563                                 return a;
564
565                         DefiniteAssignmentBitSet res;
566                         if (a.large_bits == null) {
567                                 res = new DefiniteAssignmentBitSet (a);
568                                 res.bits |= b.bits;
569                                 res.bits &= ~copy_on_write_flag;
570                                 return res;
571                         }
572
573                         res = new DefiniteAssignmentBitSet (a);
574                         res.Clone ();
575                         var dest = res.large_bits;
576                         var src = b.large_bits;
577
578                         for (int i = 0; i < dest.Length; ++i) {
579                                 dest[i] |= src[i];
580                         }
581
582                         return res;
583                 }
584
585                 public static DefiniteAssignmentBitSet And (List<DefiniteAssignmentBitSet> das)
586                 {
587                         if (das.Count == 0)
588                                 throw new ArgumentException ("Empty das");
589
590                         DefiniteAssignmentBitSet res = das[0];
591                         for (int i = 1; i < das.Count; ++i) {
592                                 res &= das[i];
593                         }
594
595                         return res;
596                 }
597
598                 bool CopyOnWrite {
599                         get {
600                                 return (bits & copy_on_write_flag) != 0;
601                         }
602                 }
603
604                 int Length {
605                         get {
606                                 return large_bits == null ? 31 : large_bits.Length * 32;
607                         }
608                 }
609
610                 public void Set (int index)
611                 {
612                         if (CopyOnWrite && !this[index])
613                                 Clone ();
614
615                         SetBit (index);
616                 }
617
618                 public void Set (int index, int length)
619                 {
620                         for (int i = 0; i < length; ++i) {
621                                 if (CopyOnWrite && !this[index + i])
622                                         Clone ();
623
624                                 SetBit (index + i);
625                         }
626                 }
627
628                 public bool this[int index] {
629                         get {
630                                 return GetBit (index);
631                         }
632                 }
633
634                 public override string ToString ()
635                 {
636                         var length = Length;
637                         StringBuilder sb = new StringBuilder (length);
638                         for (int i = 0; i < length; ++i) {
639                                 sb.Append (this[i] ? '1' : '0');
640                         }
641
642                         return sb.ToString ();
643                 }
644
645                 void Clone ()
646                 {
647                         large_bits = (int[]) large_bits.Clone ();
648                 }
649
650                 bool GetBit (int index)
651                 {
652                         return large_bits == null ?
653                                 (bits & (1 << index)) != 0 :
654                                 (large_bits[index >> 5] & (1 << (index & 31))) != 0;
655                 }
656
657                 void SetBit (int index)
658                 {
659                         if (large_bits == null)
660                                 bits = (uint) ((int) bits | (1 << index));
661                         else
662                                 large_bits[index >> 5] |= (1 << (index & 31));
663                 }
664
665                 static bool AreEqual (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b)
666                 {
667                         if (a.large_bits == null)
668                                 return (a.bits & ~copy_on_write_flag) == (b.bits & ~copy_on_write_flag);
669
670                         for (int i = 0; i < a.large_bits.Length; ++i) {
671                                 if (a.large_bits[i] != b.large_bits[i])
672                                         return false;
673                         }
674
675                         return true;
676                 }
677         }
678 }