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