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