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