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