Less static is good for my health
[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 //
11
12 using System;
13 using System.Collections.Generic;
14
15 #if STATIC
16 using IKVM.Reflection.Emit;
17 #else
18 using System.Reflection.Emit;
19 #endif
20
21 namespace Mono.CSharp
22 {
23         //
24         // Implemented by elements which can act as independent contexts
25         // during resolve phase. Used mostly for lookups.
26         //
27         public interface IMemberContext
28         {
29                 //
30                 // A scope type context, it can be inflated for generic types
31                 //
32                 TypeSpec CurrentType { get; }
33
34                 //
35                 // A scope type parameters either VAR or MVAR
36                 //
37                 TypeParameter[] CurrentTypeParameters { get; }
38
39                 //
40                 // A member definition of the context. For partial types definition use
41                 // CurrentTypeDefinition.PartialContainer otherwise the context is local
42                 //
43                 // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value
44                 //
45                 MemberCore CurrentMemberDefinition { get; }
46
47                 bool IsObsolete { get; }
48                 bool IsUnsafe { get; }
49                 bool IsStatic { get; }
50                 bool HasUnresolvedConstraints { get; }
51                 ModuleContainer Module { get; }
52
53                 string GetSignatureForError ();
54
55                 IList<MethodSpec> LookupExtensionMethod (TypeSpec extensionType, string name, int arity, ref NamespaceEntry scope);
56                 FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104);
57                 FullNamedExpression LookupNamespaceAlias (string name);
58         }
59
60         //
61         // Block or statement resolving context
62         //
63         public class BlockContext : ResolveContext
64         {
65                 FlowBranching current_flow_branching;
66
67                 TypeSpec return_type;
68
69                 /// <summary>
70                 ///   The location where return has to jump to return the
71                 ///   value
72                 /// </summary>
73                 public Label ReturnLabel;       // TODO: It's emit dependant
74
75                 /// <summary>
76                 ///   If we already defined the ReturnLabel
77                 /// </summary>
78                 public bool HasReturnLabel;
79
80                 public int FlowOffset;
81
82                 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
83                         : base (mc)
84                 {
85                         if (returnType == null)
86                                 throw new ArgumentNullException ("returnType");
87
88                         this.return_type = returnType;
89
90                         // TODO: check for null value
91                         CurrentBlock = block;
92                 }
93
94                 public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
95                         : this (rc.MemberContext, block, returnType)
96                 {
97                         if (rc.IsUnsafe)
98                                 flags |= ResolveContext.Options.UnsafeScope;
99
100                         if (rc.HasSet (ResolveContext.Options.CheckedScope))
101                                 flags |= ResolveContext.Options.CheckedScope;
102                 }
103
104                 public override FlowBranching CurrentBranching {
105                         get { return current_flow_branching; }
106                 }
107
108                 // <summary>
109                 //   Starts a new code branching.  This inherits the state of all local
110                 //   variables and parameters from the current branching.
111                 // </summary>
112                 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
113                 {
114                         current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
115                         return current_flow_branching;
116                 }
117
118                 // <summary>
119                 //   Starts a new code branching for block `block'.
120                 // </summary>
121                 public FlowBranching StartFlowBranching (Block block)
122                 {
123                         Set (Options.DoFlowAnalysis);
124
125                         current_flow_branching = FlowBranching.CreateBranching (
126                                 CurrentBranching, FlowBranching.BranchingType.Block, block, block.StartLocation);
127                         return current_flow_branching;
128                 }
129
130                 public FlowBranchingTryCatch StartFlowBranching (TryCatch stmt)
131                 {
132                         FlowBranchingTryCatch branching = new FlowBranchingTryCatch (CurrentBranching, stmt);
133                         current_flow_branching = branching;
134                         return branching;
135                 }
136
137                 public FlowBranchingException StartFlowBranching (ExceptionStatement stmt)
138                 {
139                         FlowBranchingException branching = new FlowBranchingException (CurrentBranching, stmt);
140                         current_flow_branching = branching;
141                         return branching;
142                 }
143
144                 public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt)
145                 {
146                         FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt);
147                         current_flow_branching = branching;
148                         return branching;
149                 }
150
151                 public FlowBranchingIterator StartFlowBranching (Iterator iterator, FlowBranching parent)
152                 {
153                         FlowBranchingIterator branching = new FlowBranchingIterator (parent, iterator);
154                         current_flow_branching = branching;
155                         return branching;
156                 }
157
158                 public FlowBranchingToplevel StartFlowBranching (ParametersBlock stmt, FlowBranching parent)
159                 {
160                         FlowBranchingToplevel branching = new FlowBranchingToplevel (parent, stmt);
161                         current_flow_branching = branching;
162                         return branching;
163                 }
164
165                 // <summary>
166                 //   Ends a code branching.  Merges the state of locals and parameters
167                 //   from all the children of the ending branching.
168                 // </summary>
169                 public bool EndFlowBranching ()
170                 {
171                         FlowBranching old = current_flow_branching;
172                         current_flow_branching = current_flow_branching.Parent;
173
174                         FlowBranching.UsageVector vector = current_flow_branching.MergeChild (old);
175                         return vector.IsUnreachable;
176                 }
177
178                 // <summary>
179                 //   Kills the current code branching.  This throws away any changed state
180                 //   information and should only be used in case of an error.
181                 // </summary>
182                 // FIXME: this is evil
183                 public void KillFlowBranching ()
184                 {
185                         current_flow_branching = current_flow_branching.Parent;
186                 }
187
188                 //
189                 // This method is used during the Resolution phase to flag the
190                 // need to define the ReturnLabel
191                 //
192                 public void NeedReturnLabel ()
193                 {
194                         if (!HasReturnLabel)
195                                 HasReturnLabel = true;
196                 }
197
198                 public TypeSpec ReturnType {
199                         get { return return_type; }
200                 }
201         }
202
203         //
204         // Expression resolving context
205         //
206         public class ResolveContext : IMemberContext
207         {
208                 [Flags]
209                 public enum Options
210                 {
211                         /// <summary>
212                         ///   This flag tracks the `checked' state of the compilation,
213                         ///   it controls whether we should generate code that does overflow
214                         ///   checking, or if we generate code that ignores overflows.
215                         ///
216                         ///   The default setting comes from the command line option to generate
217                         ///   checked or unchecked code plus any source code changes using the
218                         ///   checked/unchecked statements or expressions.   Contrast this with
219                         ///   the ConstantCheckState flag.
220                         /// </summary>
221                         CheckedScope = 1 << 0,
222
223                         /// <summary>
224                         ///   The constant check state is always set to `true' and cant be changed
225                         ///   from the command line.  The source code can change this setting with
226                         ///   the `checked' and `unchecked' statements and expressions. 
227                         /// </summary>
228                         ConstantCheckState = 1 << 1,
229
230                         AllCheckStateFlags = CheckedScope | ConstantCheckState,
231
232                         //
233                         // unsafe { ... } scope
234                         //
235                         UnsafeScope = 1 << 2,
236                         CatchScope = 1 << 3,
237                         FinallyScope = 1 << 4,
238                         FieldInitializerScope = 1 << 5,
239                         CompoundAssignmentScope = 1 << 6,
240                         FixedInitializerScope = 1 << 7,
241                         BaseInitializer = 1 << 8,
242
243                         //
244                         // Inside an enum definition, we do not resolve enumeration values
245                         // to their enumerations, but rather to the underlying type/value
246                         // This is so EnumVal + EnumValB can be evaluated.
247                         //
248                         // There is no "E operator + (E x, E y)", so during an enum evaluation
249                         // we relax the rules
250                         //
251                         EnumScope = 1 << 9,
252
253                         ConstantScope = 1 << 10,
254
255                         ConstructorScope = 1 << 11,
256
257                         UsingInitializerScope = 1 << 12,
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 virtual ExplicitBlock ConstructorBlock {
370                         get {
371                                 return CurrentBlock.Explicit;
372                         }
373                 }
374
375                 public virtual FlowBranching CurrentBranching {
376                         get { return null; }
377                 }
378
379                 //
380                 // The current iterator
381                 //
382                 public Iterator CurrentIterator {
383                         get { return CurrentAnonymousMethod as Iterator; }
384                 }
385
386                 public TypeSpec CurrentType {
387                         get { return MemberContext.CurrentType; }
388                 }
389
390                 public TypeParameter[] CurrentTypeParameters {
391                         get { return MemberContext.CurrentTypeParameters; }
392                 }
393
394                 public MemberCore CurrentMemberDefinition {
395                         get { return MemberContext.CurrentMemberDefinition; }
396                 }
397
398                 public bool ConstantCheckState {
399                         get { return (flags & Options.ConstantCheckState) != 0; }
400                 }
401
402                 public bool DoFlowAnalysis {
403                         get { return (flags & Options.DoFlowAnalysis) != 0; }
404                 }
405
406                 public bool HasUnresolvedConstraints {
407                         get { return false; }
408                 }
409
410                 public bool IsInProbingMode {
411                         get {
412                                 return (flags & Options.ProbingMode) != 0;
413                         }
414                 }
415
416                 public bool IsObsolete {
417                         get {
418                                 // Disables obsolete checks when probing is on
419                                 return MemberContext.IsObsolete;
420                         }
421                 }
422
423                 public bool IsStatic {
424                         get {
425                                 return MemberContext.IsStatic;
426                         }
427                 }
428
429                 public bool IsUnsafe {
430                         get {
431                                 return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
432                         }
433                 }
434
435                 public bool IsRuntimeBinder {
436                         get {
437                                 return Module.Compiler.IsRuntimeBinder;
438                         }
439                 }
440
441                 public bool IsVariableCapturingRequired {
442                         get {
443                                 return !IsInProbingMode && (CurrentBranching == null || !CurrentBranching.CurrentUsageVector.IsUnreachable);
444                         }
445                 }
446
447                 public ModuleContainer Module {
448                         get {
449                                 return MemberContext.Module;
450                         }
451                 }
452
453                 public bool OmitStructFlowAnalysis {
454                         get { return (flags & Options.OmitStructFlowAnalysis) != 0; }
455                 }
456
457                 public Report Report {
458                         get {
459                                 return Module.Compiler.Report;
460                         }
461                 }
462
463                 #endregion
464
465                 public bool MustCaptureVariable (INamedBlockVariable local)
466                 {
467                         if (CurrentAnonymousMethod == null)
468                                 return false;
469
470                         // FIXME: IsIterator is too aggressive, we should capture only if child
471                         // block contains yield
472                         if (CurrentAnonymousMethod.IsIterator)
473                                 return true;
474
475                         return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
476                 }
477
478                 public bool HasSet (Options options)
479                 {
480                         return (this.flags & options) == options;
481                 }
482
483                 public bool HasAny (Options options)
484                 {
485                         return (this.flags & options) != 0;
486                 }
487
488
489                 // Temporarily set all the given flags to the given value.  Should be used in an 'using' statement
490                 public FlagsHandle Set (Options options)
491                 {
492                         return new FlagsHandle (this, options);
493                 }
494
495                 public FlagsHandle With (Options options, bool enable)
496                 {
497                         return new FlagsHandle (this, options, enable ? options : 0);
498                 }
499
500                 #region IMemberContext Members
501
502                 public string GetSignatureForError ()
503                 {
504                         return MemberContext.GetSignatureForError ();
505                 }
506
507                 public IList<MethodSpec> LookupExtensionMethod (TypeSpec extensionType, string name, int arity, ref NamespaceEntry scope)
508                 {
509                         return MemberContext.LookupExtensionMethod (extensionType, name, arity, ref scope);
510                 }
511
512                 public FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104)
513                 {
514                         return MemberContext.LookupNamespaceOrType (name, arity, loc, ignore_cs0104);
515                 }
516
517                 public FullNamedExpression LookupNamespaceAlias (string name)
518                 {
519                         return MemberContext.LookupNamespaceAlias (name);
520                 }
521
522                 #endregion
523         }
524
525         //
526         // This class is used during the Statement.Clone operation
527         // to remap objects that have been cloned.
528         //
529         // Since blocks are cloned by Block.Clone, we need a way for
530         // expressions that must reference the block to be cloned
531         // pointing to the new cloned block.
532         //
533         public class CloneContext
534         {
535                 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
536
537                 public void AddBlockMap (Block from, Block to)
538                 {
539                         block_map.Add (from, to);
540                 }
541
542                 public Block LookupBlock (Block from)
543                 {
544                         Block result;
545                         if (!block_map.TryGetValue (from, out result)) {
546                                 result = (Block) from.Clone (this);
547                         }
548
549                         return result;
550                 }
551
552                 ///
553                 /// Remaps block to cloned copy if one exists.
554                 ///
555                 public Block RemapBlockCopy (Block from)
556                 {
557                         Block mapped_to;
558                         if (!block_map.TryGetValue (from, out mapped_to))
559                                 return from;
560
561                         return mapped_to;
562                 }
563         }
564
565         //
566         // Main compiler context
567         //
568         public class CompilerContext
569         {
570                 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
571
572                 readonly Report report;
573                 readonly BuildinTypes buildin_types;
574                 readonly CompilerSettings settings;
575
576                 public CompilerContext (CompilerSettings settings, Report report)
577                 {
578                         this.settings = settings;
579                         this.report = report;
580                         this.buildin_types = new BuildinTypes ();
581                         this.TimeReporter = DisabledTimeReporter;
582                 }
583
584                 #region Properties
585
586                 public BuildinTypes BuildinTypes {
587                         get {
588                                 return buildin_types;
589                         }
590                 }
591
592                 // Used for special handling of runtime dynamic context mostly
593                 // by error reporting but also by member accessibility checks
594                 public bool IsRuntimeBinder {
595                         get; set;
596                 }
597
598                 //
599                 // If true, it means that the compiler is executing as
600                 // in eval mode so unresolved variables are resolved in
601                 // static classes maintained by the eval engine.
602                 //
603                 public bool IsEvalutor {
604                         get; set;
605                 }
606
607                 public Report Report {
608                         get {
609                                 return report;
610                         }
611                 }
612
613                 public CompilerSettings Settings {
614                         get {
615                                 return settings;
616                         }
617                 }
618
619                 internal TimeReporter TimeReporter {
620                         get; set;
621                 }
622
623                 #endregion
624         }
625
626         //
627         // Generic code emitter context
628         //
629         public class BuilderContext
630         {
631                 [Flags]
632                 public enum Options
633                 {
634                         /// <summary>
635                         ///   This flag tracks the `checked' state of the compilation,
636                         ///   it controls whether we should generate code that does overflow
637                         ///   checking, or if we generate code that ignores overflows.
638                         ///
639                         ///   The default setting comes from the command line option to generate
640                         ///   checked or unchecked code plus any source code changes using the
641                         ///   checked/unchecked statements or expressions.   Contrast this with
642                         ///   the ConstantCheckState flag.
643                         /// </summary>
644                         CheckedScope = 1 << 0,
645
646                         /// <summary>
647                         ///   The constant check state is always set to `true' and cant be changed
648                         ///   from the command line.  The source code can change this setting with
649                         ///   the `checked' and `unchecked' statements and expressions. 
650                         /// </summary>
651                         ConstantCheckState = 1 << 1,
652
653                         AllCheckStateFlags = CheckedScope | ConstantCheckState,
654
655                         OmitDebugInfo = 1 << 2,
656
657                         ConstructorScope = 1 << 3
658                 }
659
660                 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
661                 // it's public so that we can use a struct at the callsite
662                 public struct FlagsHandle : IDisposable
663                 {
664                         BuilderContext ec;
665                         readonly Options invmask, oldval;
666
667                         public FlagsHandle (BuilderContext ec, Options flagsToSet)
668                                 : this (ec, flagsToSet, flagsToSet)
669                         {
670                         }
671
672                         internal FlagsHandle (BuilderContext ec, Options mask, Options val)
673                         {
674                                 this.ec = ec;
675                                 invmask = ~mask;
676                                 oldval = ec.flags & mask;
677                                 ec.flags = (ec.flags & invmask) | (val & mask);
678                         }
679
680                         public void Dispose ()
681                         {
682                                 ec.flags = (ec.flags & invmask) | oldval;
683                         }
684                 }
685
686                 Options flags;
687
688                 public bool HasSet (Options options)
689                 {
690                         return (this.flags & options) == options;
691                 }
692
693                 // Temporarily set all the given flags to the given value.  Should be used in an 'using' statement
694                 public FlagsHandle With (Options options, bool enable)
695                 {
696                         return new FlagsHandle (this, options, enable ? options : 0);
697                 }
698         }
699 }