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