Merge pull request #2800 from BrzVlad/feature-lazy-sweep
[mono.git] / mcs / mcs / context.cs
1 //
2 // context.cs: Various compiler contexts.
3 //
4 // Author:
5 //   Marek Safar (marek.safar@gmail.com)
6 //   Miguel de Icaza (miguel@ximian.com)
7 //
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2004-2009 Novell, Inc.
10 // Copyright 2011 Xamarin Inc.
11 //
12
13 using System;
14 using System.Collections.Generic;
15 using System.IO;
16 using System.Security.Cryptography;
17 using System.Diagnostics;
18
19 namespace Mono.CSharp
20 {
21         public enum LookupMode
22         {
23                 Normal = 0,
24                 Probing = 1,
25                 IgnoreAccessibility = 2
26         }
27
28         //
29         // Implemented by elements which can act as independent contexts
30         // during resolve phase. Used mostly for lookups.
31         //
32         public interface IMemberContext : IModuleContext
33         {
34                 //
35                 // A scope type context, it can be inflated for generic types
36                 //
37                 TypeSpec CurrentType { get; }
38
39                 //
40                 // A scope type parameters either VAR or MVAR
41                 //
42                 TypeParameters CurrentTypeParameters { get; }
43
44                 //
45                 // A member definition of the context. For partial types definition use
46                 // CurrentTypeDefinition.PartialContainer otherwise the context is local
47                 //
48                 // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value
49                 //
50                 MemberCore CurrentMemberDefinition { get; }
51
52                 bool IsObsolete { get; }
53                 bool IsUnsafe { get; }
54                 bool IsStatic { get; }
55
56                 string GetSignatureForError ();
57
58                 ExtensionMethodCandidates LookupExtensionMethod (string name, int arity);
59                 FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc);
60                 FullNamedExpression LookupNamespaceAlias (string name);
61         }
62
63         public interface IModuleContext
64         {
65                 ModuleContainer Module { get; }
66         }
67
68         //
69         // Block or statement resolving context
70         //
71         public class BlockContext : ResolveContext
72         {
73                 readonly TypeSpec return_type;
74
75                 //
76                 // Tracks the last offset used by VariableInfo
77                 //
78                 public int AssignmentInfoOffset;
79
80                 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
81                         : base (mc)
82                 {
83                         if (returnType == null)
84                                 throw new ArgumentNullException ("returnType");
85
86                         this.return_type = returnType;
87
88                         // TODO: check for null value
89                         CurrentBlock = block;
90                 }
91
92                 public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
93                         : this (rc.MemberContext, block, returnType)
94                 {
95                         if (rc.IsUnsafe)
96                                 flags |= ResolveContext.Options.UnsafeScope;
97
98                         if (rc.HasSet (ResolveContext.Options.CheckedScope))
99                                 flags |= ResolveContext.Options.CheckedScope;
100
101                         if (!rc.ConstantCheckState)
102                                 flags &= ~Options.ConstantCheckState;
103
104                         if (rc.IsInProbingMode)
105                                 flags |= ResolveContext.Options.ProbingMode;
106
107                         if (rc.HasSet (ResolveContext.Options.FieldInitializerScope))
108                                 flags |= ResolveContext.Options.FieldInitializerScope;
109
110                         if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
111                                 flags |= ResolveContext.Options.ExpressionTreeConversion;
112
113                         if (rc.HasSet (ResolveContext.Options.BaseInitializer))
114                                 flags |= ResolveContext.Options.BaseInitializer;
115                 }
116
117                 public ExceptionStatement CurrentTryBlock { get; set; }
118
119                 public TryCatch CurrentTryCatch { get; set; }
120
121                 public LoopStatement EnclosingLoop { get; set; }
122
123                 public LoopStatement EnclosingLoopOrSwitch { get; set; }
124
125                 public Switch Switch { get; set; }
126
127                 public TypeSpec ReturnType {
128                         get { return return_type; }
129                 }
130         }
131
132         //
133         // Expression resolving context
134         //
135         public class ResolveContext : IMemberContext
136         {
137                 [Flags]
138                 public enum Options
139                 {
140                         /// <summary>
141                         ///   This flag tracks the `checked' state of the compilation,
142                         ///   it controls whether we should generate code that does overflow
143                         ///   checking, or if we generate code that ignores overflows.
144                         ///
145                         ///   The default setting comes from the command line option to generate
146                         ///   checked or unchecked code plus any source code changes using the
147                         ///   checked/unchecked statements or expressions.   Contrast this with
148                         ///   the ConstantCheckState flag.
149                         /// </summary>
150                         CheckedScope = 1 << 0,
151
152                         /// <summary>
153                         ///   The constant check state is always set to `true' and cant be changed
154                         ///   from the command line.  The source code can change this setting with
155                         ///   the `checked' and `unchecked' statements and expressions. 
156                         /// </summary>
157                         ConstantCheckState = 1 << 1,
158
159                         AllCheckStateFlags = CheckedScope | ConstantCheckState,
160
161                         //
162                         // unsafe { ... } scope
163                         //
164                         UnsafeScope = 1 << 2,
165                         CatchScope = 1 << 3,
166                         FinallyScope = 1 << 4,
167                         FieldInitializerScope = 1 << 5,
168                         CompoundAssignmentScope = 1 << 6,
169                         FixedInitializerScope = 1 << 7,
170                         BaseInitializer = 1 << 8,
171
172                         //
173                         // Inside an enum definition, we do not resolve enumeration values
174                         // to their enumerations, but rather to the underlying type/value
175                         // This is so EnumVal + EnumValB can be evaluated.
176                         //
177                         // There is no "E operator + (E x, E y)", so during an enum evaluation
178                         // we relax the rules
179                         //
180                         EnumScope = 1 << 9,
181
182                         ConstantScope = 1 << 10,
183
184                         ConstructorScope = 1 << 11,
185
186                         UsingInitializerScope = 1 << 12,
187
188                         LockScope = 1 << 13,
189
190                         TryScope = 1 << 14,
191
192                         TryWithCatchScope = 1 << 15,
193
194                         DontSetConditionalAccessReceiver = 1 << 16,
195
196                         NameOfScope = 1 << 17,
197
198                         ///
199                         /// Indicates the current context is in probing mode, no errors are reported. 
200                         ///
201                         ProbingMode = 1 << 22,
202
203                         //
204                         // Return and ContextualReturn statements will set the ReturnType
205                         // value based on the expression types of each return statement
206                         // instead of the method return type which is initially null.
207                         //
208                         InferReturnType = 1 << 23,
209
210                         OmitDebuggingInfo = 1 << 24,
211
212                         ExpressionTreeConversion = 1 << 25,
213
214                         InvokeSpecialName = 1 << 26
215                 }
216
217                 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
218                 // it's public so that we can use a struct at the callsite
219                 public struct FlagsHandle : IDisposable
220                 {
221                         readonly ResolveContext ec;
222                         readonly Options invmask, oldval;
223
224                         public FlagsHandle (ResolveContext ec, Options flagsToSet)
225                                 : this (ec, flagsToSet, flagsToSet)
226                         {
227                         }
228
229                         internal FlagsHandle (ResolveContext ec, Options mask, Options val)
230                         {
231                                 this.ec = ec;
232                                 invmask = ~mask;
233                                 oldval = ec.flags & mask;
234                                 ec.flags = (ec.flags & invmask) | (val & mask);
235
236 //                              if ((mask & Options.ProbingMode) != 0)
237 //                                      ec.Report.DisableReporting ();
238                         }
239
240                         public void Dispose ()
241                         {
242 //                              if ((invmask & Options.ProbingMode) == 0)
243 //                                      ec.Report.EnableReporting ();
244
245                                 ec.flags = (ec.flags & invmask) | oldval;
246                         }
247                 }
248
249                 protected Options flags;
250
251                 //
252                 // Whether we are inside an anonymous method.
253                 //
254                 public AnonymousExpression CurrentAnonymousMethod;
255
256                 //
257                 // Holds a varible used during collection or object initialization.
258                 //
259                 public Expression CurrentInitializerVariable;
260
261                 public Block CurrentBlock;
262
263                 public readonly IMemberContext MemberContext;
264
265                 public ResolveContext (IMemberContext mc)
266                 {
267                         if (mc == null)
268                                 throw new ArgumentNullException ();
269
270                         MemberContext = mc;
271
272                         //
273                         // The default setting comes from the command line option
274                         //
275                         if (mc.Module.Compiler.Settings.Checked)
276                                 flags |= Options.CheckedScope;
277
278                         //
279                         // The constant check state is always set to true
280                         //
281                         flags |= Options.ConstantCheckState;
282                 }
283
284                 public ResolveContext (IMemberContext mc, Options options)
285                         : this (mc)
286                 {
287                         flags |= options;
288                 }
289
290                 #region Properties
291
292                 public BuiltinTypes BuiltinTypes {
293                         get {
294                                 return MemberContext.Module.Compiler.BuiltinTypes;
295                         }
296                 }
297
298                 public virtual ExplicitBlock ConstructorBlock {
299                         get {
300                                 return CurrentBlock.Explicit;
301                         }
302                 }
303
304                 //
305                 // The current iterator
306                 //
307                 public Iterator CurrentIterator {
308                         get { return CurrentAnonymousMethod as Iterator; }
309                 }
310
311                 public TypeSpec CurrentType {
312                         get { return MemberContext.CurrentType; }
313                 }
314
315                 public TypeParameters CurrentTypeParameters {
316                         get { return MemberContext.CurrentTypeParameters; }
317                 }
318
319                 public MemberCore CurrentMemberDefinition {
320                         get { return MemberContext.CurrentMemberDefinition; }
321                 }
322
323                 public bool ConstantCheckState {
324                         get { return (flags & Options.ConstantCheckState) != 0; }
325                 }
326
327                 public bool IsInProbingMode {
328                         get {
329                                 return (flags & Options.ProbingMode) != 0;
330                         }
331                 }
332
333                 public bool IsObsolete {
334                         get {
335                                 // Disables obsolete checks when probing is on
336                                 return MemberContext.IsObsolete;
337                         }
338                 }
339
340                 public bool IsStatic {
341                         get {
342                                 return MemberContext.IsStatic;
343                         }
344                 }
345
346                 public bool IsUnsafe {
347                         get {
348                                 return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
349                         }
350                 }
351
352                 public bool IsRuntimeBinder {
353                         get {
354                                 return Module.Compiler.IsRuntimeBinder;
355                         }
356                 }
357
358                 public bool IsVariableCapturingRequired {
359                         get {
360                                 return !IsInProbingMode;
361                         }
362                 }
363
364                 public ModuleContainer Module {
365                         get {
366                                 return MemberContext.Module;
367                         }
368                 }
369
370                 public Report Report {
371                         get {
372                                 return Module.Compiler.Report;
373                         }
374                 }
375
376                 #endregion
377
378                 public bool MustCaptureVariable (INamedBlockVariable local)
379                 {
380                         if (CurrentAnonymousMethod == null)
381                                 return false;
382
383                         //
384                         // Capture only if this or any of child blocks contain yield
385                         // or it's a parameter
386                         //
387                         if (CurrentAnonymousMethod.IsIterator)
388                                 return local.IsParameter || local.Block.Explicit.HasYield;
389
390                         //
391                         // Capture only if this or any of child blocks contain await
392                         // or it's a parameter or we need to access variable from 
393                         // different parameter block
394                         //
395                         if (CurrentAnonymousMethod is AsyncInitializer)
396                                 return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait ||
397                                         local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
398
399                         return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
400                 }
401
402                 public bool HasSet (Options options)
403                 {
404                         return (this.flags & options) == options;
405                 }
406
407                 public bool HasAny (Options options)
408                 {
409                         return (this.flags & options) != 0;
410                 }
411
412
413                 // Temporarily set all the given flags to the given value.  Should be used in an 'using' statement
414                 public FlagsHandle Set (Options options)
415                 {
416                         return new FlagsHandle (this, options);
417                 }
418
419                 public FlagsHandle With (Options options, bool enable)
420                 {
421                         return new FlagsHandle (this, options, enable ? options : 0);
422                 }
423
424                 #region IMemberContext Members
425
426                 public string GetSignatureForError ()
427                 {
428                         return MemberContext.GetSignatureForError ();
429                 }
430
431                 public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity)
432                 {
433                         return MemberContext.LookupExtensionMethod (name, arity);
434                 }
435
436                 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
437                 {
438                         return MemberContext.LookupNamespaceOrType (name, arity, mode, loc);
439                 }
440
441                 public FullNamedExpression LookupNamespaceAlias (string name)
442                 {
443                         return MemberContext.LookupNamespaceAlias (name);
444                 }
445
446                 #endregion
447         }
448
449         public class FlowAnalysisContext
450         {
451                 readonly CompilerContext ctx;
452
453                 public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength)
454                 {
455                         this.ctx = ctx;
456                         this.ParametersBlock = parametersBlock;
457
458                         DefiniteAssignment = definiteAssignmentLength == 0 ?
459                                 DefiniteAssignmentBitSet.Empty :
460                                 new DefiniteAssignmentBitSet (definiteAssignmentLength);
461                 }
462
463                 public DefiniteAssignmentBitSet DefiniteAssignment { get; set; }
464
465                 public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; }
466
467                 public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; }
468
469                 Dictionary<Statement, List<DefiniteAssignmentBitSet>> LabelStack { get; set; }
470
471                 public ParametersBlock ParametersBlock { get; set; }
472
473                 public Report Report {
474                         get {
475                                 return ctx.Report;
476                         }
477                 }
478
479                 public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; }
480
481                 public TryFinally TryFinally { get; set; }
482
483                 public bool UnreachableReported { get; set; }
484
485                 public bool AddReachedLabel (Statement label)
486                 {
487                         List<DefiniteAssignmentBitSet> das;
488                         if (LabelStack == null) {
489                                 LabelStack = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
490                                 das = null;
491                         } else {
492                                 LabelStack.TryGetValue (label, out das);
493                         }
494
495                         if (das == null) {
496                                 das = new List<DefiniteAssignmentBitSet> ();
497                                 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
498                                 LabelStack.Add (label, das);
499                                 return false;
500                         }
501
502                         foreach (var existing in das) {
503                                 if (DefiniteAssignmentBitSet.IsIncluded (existing, DefiniteAssignment))
504                                         return true;
505                         }
506
507                         if (DefiniteAssignment == DefiniteAssignmentBitSet.Empty)
508                                 das.Add (DefiniteAssignment);
509                         else
510                                 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
511
512                         return false;
513                 }
514
515                 public DefiniteAssignmentBitSet BranchDefiniteAssignment ()
516                 {
517                         return BranchDefiniteAssignment (DefiniteAssignment);
518                 }
519
520                 public DefiniteAssignmentBitSet BranchDefiniteAssignment (DefiniteAssignmentBitSet da)
521                 {
522                         if (da != DefiniteAssignmentBitSet.Empty) {
523                                 DefiniteAssignment = new DefiniteAssignmentBitSet (da);
524                         }
525
526                         return da;
527                 }
528
529                 public bool IsDefinitelyAssigned (VariableInfo variable)
530                 {
531                         return variable.IsAssigned (DefiniteAssignment);
532                 }
533
534                 public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
535                 {
536                         return variable.IsStructFieldAssigned (DefiniteAssignment, name);
537                 }
538
539                 public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
540                 {
541                         variable.SetAssigned (DefiniteAssignment, generatedAssignment);
542                 }
543
544                 public void SetStructFieldAssigned (VariableInfo variable, string name)
545                 {
546                         variable.SetStructFieldAssigned (DefiniteAssignment, name);
547                 }
548         }
549
550
551         //
552         // This class is used during the Statement.Clone operation
553         // to remap objects that have been cloned.
554         //
555         // Since blocks are cloned by Block.Clone, we need a way for
556         // expressions that must reference the block to be cloned
557         // pointing to the new cloned block.
558         //
559         public class CloneContext
560         {
561                 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
562
563                 public void AddBlockMap (Block from, Block to)
564                 {
565                         block_map.Add (from, to);
566                 }
567
568                 public Block LookupBlock (Block from)
569                 {
570                         Block result;
571                         if (!block_map.TryGetValue (from, out result)) {
572                                 result = (Block) from.Clone (this);
573                         }
574
575                         return result;
576                 }
577
578                 ///
579                 /// Remaps block to cloned copy if one exists.
580                 ///
581                 public Block RemapBlockCopy (Block from)
582                 {
583                         Block mapped_to;
584                         if (!block_map.TryGetValue (from, out mapped_to))
585                                 return from;
586
587                         return mapped_to;
588                 }
589         }
590
591         //
592         // Main compiler context
593         //
594         public class CompilerContext
595         {
596                 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
597
598                 readonly Report report;
599                 readonly BuiltinTypes builtin_types;
600                 readonly CompilerSettings settings;
601
602                 Dictionary<string, SourceFile> all_source_files;
603
604                 public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
605                 {
606                         this.settings = settings;
607                         this.report = new Report (this, reportPrinter);
608                         this.builtin_types = new BuiltinTypes ();
609                         this.TimeReporter = DisabledTimeReporter;
610                 }
611
612                 #region Properties
613
614                 public BuiltinTypes BuiltinTypes {
615                         get {
616                                 return builtin_types;
617                         }
618                 }
619
620                 // Used for special handling of runtime dynamic context mostly
621                 // by error reporting but also by member accessibility checks
622                 public bool IsRuntimeBinder {
623                         get; set;
624                 }
625
626                 public Report Report {
627                         get {
628                                 return report;
629                         }
630                 }
631
632                 public CompilerSettings Settings {
633                         get {
634                                 return settings;
635                         }
636                 }
637
638                 public List<SourceFile> SourceFiles {
639                         get {
640                                 return settings.SourceFiles;
641                         }
642                 }
643
644                 internal TimeReporter TimeReporter {
645                         get; set;
646                 }
647
648                 #endregion
649
650                 //
651                 // This is used when we encounter a #line preprocessing directive during parsing
652                 // to register additional source file names
653                 //
654                 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
655                 {
656                         if (all_source_files == null) {
657                                 all_source_files = new Dictionary<string, SourceFile> ();
658                                 foreach (var source in SourceFiles)
659                                         all_source_files[source.FullPathName] = source;
660                         }
661
662                         string path;
663                         if (!Path.IsPathRooted (name)) {
664                                 var loc = comp_unit.SourceFile;
665                                 string root = Path.GetDirectoryName (loc.FullPathName);
666                                 path = Path.GetFullPath (Path.Combine (root, name));
667                                 var dir = Path.GetDirectoryName (loc.Name);
668                                 if (!string.IsNullOrEmpty (dir))
669                                         name = Path.Combine (dir, name);
670                         } else
671                                 path = name;
672
673                         SourceFile retval;
674                         if (all_source_files.TryGetValue (path, out retval))
675                                 return retval;
676
677                         retval = new SourceFile (name, path, all_source_files.Count + 1);
678                         Location.AddFile (retval);
679                         all_source_files.Add (path, retval);
680                         return retval;
681                 }
682         }
683
684         //
685         // Generic code emitter context
686         //
687         public class BuilderContext
688         {
689                 [Flags]
690                 public enum Options
691                 {
692                         /// <summary>
693                         ///   This flag tracks the `checked' state of the compilation,
694                         ///   it controls whether we should generate code that does overflow
695                         ///   checking, or if we generate code that ignores overflows.
696                         ///
697                         ///   The default setting comes from the command line option to generate
698                         ///   checked or unchecked code plus any source code changes using the
699                         ///   checked/unchecked statements or expressions.   Contrast this with
700                         ///   the ConstantCheckState flag.
701                         /// </summary>
702                         CheckedScope = 1 << 0,
703
704                         AccurateDebugInfo = 1 << 1,
705
706                         OmitDebugInfo = 1 << 2,
707
708                         ConstructorScope = 1 << 3,
709
710                         AsyncBody = 1 << 4,
711                 }
712
713                 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
714                 // it's public so that we can use a struct at the callsite
715                 public struct FlagsHandle : IDisposable
716                 {
717                         readonly BuilderContext ec;
718                         readonly Options invmask, oldval;
719
720                         public FlagsHandle (BuilderContext ec, Options flagsToSet)
721                                 : this (ec, flagsToSet, flagsToSet)
722                         {
723                         }
724
725                         internal FlagsHandle (BuilderContext ec, Options mask, Options val)
726                         {
727                                 this.ec = ec;
728                                 invmask = ~mask;
729                                 oldval = ec.flags & mask;
730                                 ec.flags = (ec.flags & invmask) | (val & mask);
731                         }
732
733                         public void Dispose ()
734                         {
735                                 ec.flags = (ec.flags & invmask) | oldval;
736                         }
737                 }
738
739                 protected Options flags;
740
741                 public bool HasSet (Options options)
742                 {
743                         return (this.flags & options) == options;
744                 }
745
746                 // Temporarily set all the given flags to the given value.  Should be used in an 'using' statement
747                 public FlagsHandle With (Options options, bool enable)
748                 {
749                         return new FlagsHandle (this, options, enable ? options : 0);
750                 }
751         }
752
753         //
754         // Parser session objects. We could recreate all these objects for each parser
755         // instance but the best parser performance the session object can be reused
756         //
757         public class ParserSession
758         {
759                 MD5 md5;
760
761                 public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
762                 public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
763                 public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
764                 public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
765                 public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
766
767                 public LocationsBag LocationsBag { get; set; }
768                 public bool UseJayGlobalArrays { get; set; }
769                 public LocatedToken[] LocatedTokens { get; set; }
770
771                 public MD5 GetChecksumAlgorithm ()
772                 {
773                         return md5 ?? (md5 = MD5.Create ());
774                 }
775         }
776 }