copied mono-api-diff.cs from mono-2-2 branch so new patch can be applied and history...
[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 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                 #region IMemberContext Members
455
456                 public string GetSignatureForError ()
457                 {
458                         return MemberContext.GetSignatureForError ();
459                 }
460
461                 public bool IsObsolete {
462                         get {
463                                 // Disables obsolete checks when probing is on
464                                 return IsInProbingMode || MemberContext.IsObsolete;
465                         }
466                 }
467
468                 public bool IsStatic {
469                         get { return MemberContext.IsStatic; }
470                 }
471
472                 public bool IsUnsafe {
473                         get { return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe; }
474                 }
475
476                 public ExtensionMethodGroupExpr LookupExtensionMethod (Type extensionType, string name, Location loc)
477                 {
478                         return MemberContext.LookupExtensionMethod (extensionType, name, loc);
479                 }
480
481                 public FullNamedExpression LookupNamespaceOrType (string name, Location loc, bool ignore_cs0104)
482                 {
483                         return MemberContext.LookupNamespaceOrType (name, loc, ignore_cs0104);
484                 }
485
486                 public FullNamedExpression LookupNamespaceAlias (string name)
487                 {
488                         return MemberContext.LookupNamespaceAlias (name);
489                 }
490
491                 #endregion
492         }
493
494         //
495         // This class is used during the Statement.Clone operation
496         // to remap objects that have been cloned.
497         //
498         // Since blocks are cloned by Block.Clone, we need a way for
499         // expressions that must reference the block to be cloned
500         // pointing to the new cloned block.
501         //
502         public class CloneContext
503         {
504                 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
505                 Dictionary<LocalInfo, LocalInfo> variable_map;
506
507                 public void AddBlockMap (Block from, Block to)
508                 {
509                         if (block_map.ContainsKey (from))
510                                 return;
511                         block_map[from] = to;
512                 }
513
514                 public Block LookupBlock (Block from)
515                 {
516                         Block result;
517                         if (!block_map.TryGetValue (from, out result)) {
518                                 result = (Block) from.Clone (this);
519                                 block_map [from] = result;
520                         }
521
522                         return result;
523                 }
524
525                 ///
526                 /// Remaps block to cloned copy if one exists.
527                 ///
528                 public Block RemapBlockCopy (Block from)
529                 {
530                         Block mapped_to;
531                         if (!block_map.TryGetValue (from, out mapped_to))
532                                 return from;
533
534                         return mapped_to;
535                 }
536
537                 public void AddVariableMap (LocalInfo from, LocalInfo to)
538                 {
539                         if (variable_map == null)
540                                 variable_map = new Dictionary<LocalInfo, LocalInfo> ();
541                         else if (variable_map.ContainsKey (from))
542                                 return;
543
544                         variable_map[from] = to;
545                 }
546
547                 public LocalInfo LookupVariable (LocalInfo from)
548                 {
549                         try {
550                                 return variable_map[from];
551                         } catch (KeyNotFoundException) {
552                                 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
553                         }
554                 }
555         }
556
557         //
558         // Main compiler context
559         //
560         public class CompilerContext
561         {
562                 readonly Report report;
563
564                 public CompilerContext (Report report)
565                 {
566                         this.report = report;
567                 }
568
569                 public bool IsRuntimeBinder { get; set; }
570
571                 public Report Report {
572                         get { return report; }
573                 }
574
575                 //public PredefinedAttributes PredefinedAttributes {
576                 //    get { throw new NotImplementedException (); }
577                 //}
578         }
579
580         //
581         // Generic code emitter context
582         //
583         public class BuilderContext
584         {
585                 [Flags]
586                 public enum Options
587                 {
588                         /// <summary>
589                         ///   This flag tracks the `checked' state of the compilation,
590                         ///   it controls whether we should generate code that does overflow
591                         ///   checking, or if we generate code that ignores overflows.
592                         ///
593                         ///   The default setting comes from the command line option to generate
594                         ///   checked or unchecked code plus any source code changes using the
595                         ///   checked/unchecked statements or expressions.   Contrast this with
596                         ///   the ConstantCheckState flag.
597                         /// </summary>
598                         CheckedScope = 1 << 0,
599
600                         /// <summary>
601                         ///   The constant check state is always set to `true' and cant be changed
602                         ///   from the command line.  The source code can change this setting with
603                         ///   the `checked' and `unchecked' statements and expressions. 
604                         /// </summary>
605                         ConstantCheckState = 1 << 1,
606
607                         AllCheckStateFlags = CheckedScope | ConstantCheckState,
608
609                         OmitDebugInfo = 1 << 2,
610
611                         ConstructorScope = 1 << 3
612                 }
613
614                 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
615                 // it's public so that we can use a struct at the callsite
616                 public struct FlagsHandle : IDisposable
617                 {
618                         BuilderContext ec;
619                         readonly Options invmask, oldval;
620
621                         public FlagsHandle (BuilderContext ec, Options flagsToSet)
622                                 : this (ec, flagsToSet, flagsToSet)
623                         {
624                         }
625
626                         internal FlagsHandle (BuilderContext ec, Options mask, Options val)
627                         {
628                                 this.ec = ec;
629                                 invmask = ~mask;
630                                 oldval = ec.flags & mask;
631                                 ec.flags = (ec.flags & invmask) | (val & mask);
632                         }
633
634                         public void Dispose ()
635                         {
636                                 ec.flags = (ec.flags & invmask) | oldval;
637                         }
638                 }
639
640                 Options flags;
641
642                 public bool HasSet (Options options)
643                 {
644                         return (this.flags & options) == options;
645                 }
646
647                 // Temporarily set all the given flags to the given value.  Should be used in an 'using' statement
648                 public FlagsHandle With (Options options, bool enable)
649                 {
650                         return new FlagsHandle (this, options, enable ? options : 0);
651                 }
652         }
653 }