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