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