2010-05-27 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / context.cs
blob89db3023bd067ddbbea54fa502efd056c3665c06
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.
12 using System;
13 using System.Collections.Generic;
14 using System.Reflection.Emit;
16 namespace Mono.CSharp
19 // Implemented by elements which can act as independent contexts
20 // during resolve phase. Used mostly for lookups.
22 public interface IMemberContext
25 // A scope type context, it can be inflated for generic types
27 TypeSpec CurrentType { get; }
30 // A scope type parameters either VAR or MVAR
32 TypeParameter[] CurrentTypeParameters { get; }
35 // A member definition of the context. For partial types definition use
36 // CurrentTypeDefinition.PartialContainer otherwise the context is local
38 MemberCore CurrentMemberDefinition { get; }
40 bool IsObsolete { get; }
41 bool IsUnsafe { get; }
42 bool IsStatic { get; }
43 bool HasUnresolvedConstraints { get; }
45 string GetSignatureForError ();
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);
51 CompilerContext Compiler { get; }
55 // Block or statement resolving context
57 public class BlockContext : ResolveContext
59 FlowBranching current_flow_branching;
61 public TypeInferenceContext ReturnTypeInference;
63 TypeSpec return_type;
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
71 /// <summary>
72 /// If we already defined the ReturnLabel
73 /// </summary>
74 public bool HasReturnLabel;
76 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
77 : base (mc)
79 if (returnType == null)
80 throw new ArgumentNullException ("returnType");
82 this.return_type = returnType;
84 // TODO: check for null value
85 CurrentBlock = block;
88 public override FlowBranching CurrentBranching {
89 get { return current_flow_branching; }
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)
98 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
99 return current_flow_branching;
102 // <summary>
103 // Starts a new code branching for block `block'.
104 // </summary>
105 public FlowBranching StartFlowBranching (Block block)
107 Set (Options.DoFlowAnalysis);
109 current_flow_branching = FlowBranching.CreateBranching (
110 CurrentBranching, FlowBranching.BranchingType.Block, block, block.StartLocation);
111 return current_flow_branching;
114 public FlowBranchingTryCatch StartFlowBranching (TryCatch stmt)
116 FlowBranchingTryCatch branching = new FlowBranchingTryCatch (CurrentBranching, stmt);
117 current_flow_branching = branching;
118 return branching;
121 public FlowBranchingException StartFlowBranching (ExceptionStatement stmt)
123 FlowBranchingException branching = new FlowBranchingException (CurrentBranching, stmt);
124 current_flow_branching = branching;
125 return branching;
128 public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt)
130 FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt);
131 current_flow_branching = branching;
132 return branching;
135 public FlowBranchingIterator StartFlowBranching (Iterator iterator)
137 FlowBranchingIterator branching = new FlowBranchingIterator (CurrentBranching, iterator);
138 current_flow_branching = branching;
139 return branching;
142 public FlowBranchingToplevel StartFlowBranching (ToplevelBlock stmt, FlowBranching parent)
144 FlowBranchingToplevel branching = new FlowBranchingToplevel (parent, stmt);
145 current_flow_branching = branching;
146 return branching;
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 ()
155 FlowBranching old = current_flow_branching;
156 current_flow_branching = current_flow_branching.Parent;
158 FlowBranching.UsageVector vector = current_flow_branching.MergeChild (old);
159 return vector.IsUnreachable;
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 ()
169 current_flow_branching = current_flow_branching.Parent;
173 // This method is used during the Resolution phase to flag the
174 // need to define the ReturnLabel
176 public void NeedReturnLabel ()
178 if (!HasReturnLabel)
179 HasReturnLabel = true;
182 public TypeSpec ReturnType {
183 get { return return_type; }
188 // Expression resolving context
190 public class ResolveContext : IMemberContext
192 [Flags]
193 public enum Options
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.
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,
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,
214 AllCheckStateFlags = CheckedScope | ConstantCheckState,
217 // unsafe { ... } scope
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,
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.
232 // There is no "E operator + (E x, E y)", so during an enum evaluation
233 // we relax the rules
235 EnumScope = 1 << 9,
237 ConstantScope = 1 << 10,
239 ConstructorScope = 1 << 11,
241 /// <summary>
242 /// Whether control flow analysis is enabled
243 /// </summary>
244 DoFlowAnalysis = 1 << 20,
246 /// <summary>
247 /// Whether control flow analysis is disabled on structs
248 /// (only meaningful when DoFlowAnalysis is set)
249 /// </summary>
250 OmitStructFlowAnalysis = 1 << 21,
253 /// Indicates the current context is in probing mode, no errors are reported.
255 ProbingMode = 1 << 22,
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.
262 InferReturnType = 1 << 23,
264 OmitDebuggingInfo = 1 << 24,
266 ExpressionTreeConversion = 1 << 25,
268 InvokeSpecialName = 1 << 26
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
275 ResolveContext ec;
276 readonly Options invmask, oldval;
278 public FlagsHandle (ResolveContext ec, Options flagsToSet)
279 : this (ec, flagsToSet, flagsToSet)
283 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
285 this.ec = ec;
286 invmask = ~mask;
287 oldval = ec.flags & mask;
288 ec.flags = (ec.flags & invmask) | (val & mask);
290 // if ((mask & Options.ProbingMode) != 0)
291 // ec.Report.DisableReporting ();
294 public void Dispose ()
296 // if ((invmask & Options.ProbingMode) == 0)
297 // ec.Report.EnableReporting ();
299 ec.flags = (ec.flags & invmask) | oldval;
303 Options flags;
306 // Whether we are inside an anonymous method.
308 public AnonymousExpression CurrentAnonymousMethod;
311 // Holds a varible used during collection or object initialization.
313 public Expression CurrentInitializerVariable;
315 public Block CurrentBlock;
317 public IMemberContext MemberContext;
319 /// <summary>
320 /// If this is non-null, points to the current switch statement
321 /// </summary>
322 public Switch Switch;
324 public ResolveContext (IMemberContext mc)
326 if (mc == null)
327 throw new ArgumentNullException ();
329 MemberContext = mc;
332 // The default setting comes from the command line option
334 if (RootContext.Checked)
335 flags |= Options.CheckedScope;
338 // The constant check state is always set to true
340 flags |= Options.ConstantCheckState;
343 public ResolveContext (IMemberContext mc, Options options)
344 : this (mc)
346 flags |= options;
349 public CompilerContext Compiler {
350 get { return MemberContext.Compiler; }
353 public virtual FlowBranching CurrentBranching {
354 get { return null; }
358 // The current iterator
360 public Iterator CurrentIterator {
361 get { return CurrentAnonymousMethod as Iterator; }
364 public TypeSpec CurrentType {
365 get { return MemberContext.CurrentType; }
368 public TypeParameter[] CurrentTypeParameters {
369 get { return MemberContext.CurrentTypeParameters; }
372 public MemberCore CurrentMemberDefinition {
373 get { return MemberContext.CurrentMemberDefinition; }
376 public bool ConstantCheckState {
377 get { return (flags & Options.ConstantCheckState) != 0; }
380 public bool DoFlowAnalysis {
381 get { return (flags & Options.DoFlowAnalysis) != 0; }
384 public bool HasUnresolvedConstraints {
385 get { return false; }
388 public bool IsInProbingMode {
389 get { return (flags & Options.ProbingMode) != 0; }
392 public bool IsVariableCapturingRequired {
393 get {
394 return !IsInProbingMode && (CurrentBranching == null || !CurrentBranching.CurrentUsageVector.IsUnreachable);
398 public bool OmitStructFlowAnalysis {
399 get { return (flags & Options.OmitStructFlowAnalysis) != 0; }
402 // TODO: Merge with CompilerGeneratedThis
403 public Expression GetThis (Location loc)
405 This my_this = new This (loc);
407 if (!my_this.ResolveBase (this))
408 my_this = null;
410 return my_this;
413 public bool MustCaptureVariable (LocalInfo local)
415 if (CurrentAnonymousMethod == null)
416 return false;
418 // FIXME: IsIterator is too aggressive, we should capture only if child
419 // block contains yield
420 if (CurrentAnonymousMethod.IsIterator)
421 return true;
423 return local.Block.Toplevel != CurrentBlock.Toplevel;
426 public bool HasSet (Options options)
428 return (this.flags & options) == options;
431 public bool HasAny (Options options)
433 return (this.flags & options) != 0;
436 public Report Report {
437 get {
438 return Compiler.Report;
442 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
443 public FlagsHandle Set (Options options)
445 return new FlagsHandle (this, options);
448 public FlagsHandle With (Options options, bool enable)
450 return new FlagsHandle (this, options, enable ? options : 0);
453 #region IMemberContext Members
455 public string GetSignatureForError ()
457 return MemberContext.GetSignatureForError ();
460 public bool IsObsolete {
461 get {
462 // Disables obsolete checks when probing is on
463 return IsInProbingMode || MemberContext.IsObsolete;
467 public bool IsStatic {
468 get { return MemberContext.IsStatic; }
471 public bool IsUnsafe {
472 get { return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe; }
475 public ExtensionMethodGroupExpr LookupExtensionMethod (TypeSpec extensionType, string name, int arity, Location loc)
477 return MemberContext.LookupExtensionMethod (extensionType, name, arity, loc);
480 public FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104)
482 return MemberContext.LookupNamespaceOrType (name, arity, loc, ignore_cs0104);
485 public FullNamedExpression LookupNamespaceAlias (string name)
487 return MemberContext.LookupNamespaceAlias (name);
490 #endregion
494 // This class is used during the Statement.Clone operation
495 // to remap objects that have been cloned.
497 // Since blocks are cloned by Block.Clone, we need a way for
498 // expressions that must reference the block to be cloned
499 // pointing to the new cloned block.
501 public class CloneContext
503 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
504 Dictionary<LocalInfo, LocalInfo> variable_map;
506 public void AddBlockMap (Block from, Block to)
508 if (block_map.ContainsKey (from))
509 return;
510 block_map[from] = to;
513 public Block LookupBlock (Block from)
515 Block result;
516 if (!block_map.TryGetValue (from, out result)) {
517 result = (Block) from.Clone (this);
518 block_map [from] = result;
521 return result;
525 /// Remaps block to cloned copy if one exists.
527 public Block RemapBlockCopy (Block from)
529 Block mapped_to;
530 if (!block_map.TryGetValue (from, out mapped_to))
531 return from;
533 return mapped_to;
536 public void AddVariableMap (LocalInfo from, LocalInfo to)
538 if (variable_map == null)
539 variable_map = new Dictionary<LocalInfo, LocalInfo> ();
540 else if (variable_map.ContainsKey (from))
541 return;
543 variable_map[from] = to;
546 public LocalInfo LookupVariable (LocalInfo from)
548 try {
549 return variable_map[from];
550 } catch (KeyNotFoundException) {
551 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
557 // Main compiler context
559 public class CompilerContext
561 readonly Report report;
563 public CompilerContext (Report report)
565 this.report = report;
568 public bool IsRuntimeBinder { get; set; }
570 public Report Report {
571 get { return report; }
574 //public PredefinedAttributes PredefinedAttributes {
575 // get { throw new NotImplementedException (); }
580 // Generic code emitter context
582 public class BuilderContext
584 [Flags]
585 public enum Options
587 /// <summary>
588 /// This flag tracks the `checked' state of the compilation,
589 /// it controls whether we should generate code that does overflow
590 /// checking, or if we generate code that ignores overflows.
592 /// The default setting comes from the command line option to generate
593 /// checked or unchecked code plus any source code changes using the
594 /// checked/unchecked statements or expressions. Contrast this with
595 /// the ConstantCheckState flag.
596 /// </summary>
597 CheckedScope = 1 << 0,
599 /// <summary>
600 /// The constant check state is always set to `true' and cant be changed
601 /// from the command line. The source code can change this setting with
602 /// the `checked' and `unchecked' statements and expressions.
603 /// </summary>
604 ConstantCheckState = 1 << 1,
606 AllCheckStateFlags = CheckedScope | ConstantCheckState,
608 OmitDebugInfo = 1 << 2,
610 ConstructorScope = 1 << 3
613 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
614 // it's public so that we can use a struct at the callsite
615 public struct FlagsHandle : IDisposable
617 BuilderContext ec;
618 readonly Options invmask, oldval;
620 public FlagsHandle (BuilderContext ec, Options flagsToSet)
621 : this (ec, flagsToSet, flagsToSet)
625 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
627 this.ec = ec;
628 invmask = ~mask;
629 oldval = ec.flags & mask;
630 ec.flags = (ec.flags & invmask) | (val & mask);
633 public void Dispose ()
635 ec.flags = (ec.flags & invmask) | oldval;
639 Options flags;
641 public bool HasSet (Options options)
643 return (this.flags & options) == options;
646 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
647 public FlagsHandle With (Options options, bool enable)
649 return new FlagsHandle (this, options, enable ? options : 0);