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