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