2009-12-09 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / context.cs
blob8a56cdcfb4953552dea7e1a60947d7206f286eb0
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 Type CurrentType { get; }
30 // A scope type parameters either VAR or MVAR
32 TypeParameter[] CurrentTypeParameters { get; }
35 // A type definition of the type context. For partial types definition use
36 // CurrentTypeDefinition.PartialContainer otherwise the context is local
38 // TODO: CurrentType.Definition
40 TypeContainer CurrentTypeDefinition { get; }
42 bool IsObsolete { get; }
43 bool IsUnsafe { get; }
44 bool IsStatic { get; }
46 string GetSignatureForError ();
48 ExtensionMethodGroupExpr LookupExtensionMethod (Type extensionType, string name, Location loc);
49 FullNamedExpression LookupNamespaceOrType (string name, Location loc, bool ignore_cs0104);
50 FullNamedExpression LookupNamespaceAlias (string name);
52 CompilerContext Compiler { get; }
56 // Block or statement resolving context
58 public class BlockContext : ResolveContext
60 FlowBranching current_flow_branching;
62 public TypeInferenceContext ReturnTypeInference;
64 Type return_type;
66 /// <summary>
67 /// The location where return has to jump to return the
68 /// value
69 /// </summary>
70 public Label ReturnLabel; // TODO: It's emit dependant
72 /// <summary>
73 /// If we already defined the ReturnLabel
74 /// </summary>
75 public bool HasReturnLabel;
77 public BlockContext (IMemberContext mc, ExplicitBlock block, Type returnType)
78 : base (mc)
80 if (returnType == null)
81 throw new ArgumentNullException ("returnType");
83 this.return_type = returnType;
85 // TODO: check for null value
86 CurrentBlock = block;
89 public override FlowBranching CurrentBranching {
90 get { return current_flow_branching; }
93 // <summary>
94 // Starts a new code branching. This inherits the state of all local
95 // variables and parameters from the current branching.
96 // </summary>
97 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
99 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
100 return current_flow_branching;
103 // <summary>
104 // Starts a new code branching for block `block'.
105 // </summary>
106 public FlowBranching StartFlowBranching (Block block)
108 Set (Options.DoFlowAnalysis);
110 current_flow_branching = FlowBranching.CreateBranching (
111 CurrentBranching, FlowBranching.BranchingType.Block, block, block.StartLocation);
112 return current_flow_branching;
115 public FlowBranchingTryCatch StartFlowBranching (TryCatch stmt)
117 FlowBranchingTryCatch branching = new FlowBranchingTryCatch (CurrentBranching, stmt);
118 current_flow_branching = branching;
119 return branching;
122 public FlowBranchingException StartFlowBranching (ExceptionStatement stmt)
124 FlowBranchingException branching = new FlowBranchingException (CurrentBranching, stmt);
125 current_flow_branching = branching;
126 return branching;
129 public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt)
131 FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt);
132 current_flow_branching = branching;
133 return branching;
136 public FlowBranchingIterator StartFlowBranching (Iterator iterator)
138 FlowBranchingIterator branching = new FlowBranchingIterator (CurrentBranching, iterator);
139 current_flow_branching = branching;
140 return branching;
143 public FlowBranchingToplevel StartFlowBranching (ToplevelBlock stmt, FlowBranching parent)
145 FlowBranchingToplevel branching = new FlowBranchingToplevel (parent, stmt);
146 current_flow_branching = branching;
147 return branching;
150 // <summary>
151 // Ends a code branching. Merges the state of locals and parameters
152 // from all the children of the ending branching.
153 // </summary>
154 public bool EndFlowBranching ()
156 FlowBranching old = current_flow_branching;
157 current_flow_branching = current_flow_branching.Parent;
159 FlowBranching.UsageVector vector = current_flow_branching.MergeChild (old);
160 return vector.IsUnreachable;
163 // <summary>
164 // Kills the current code branching. This throws away any changed state
165 // information and should only be used in case of an error.
166 // </summary>
167 // FIXME: this is evil
168 public void KillFlowBranching ()
170 current_flow_branching = current_flow_branching.Parent;
174 // This method is used during the Resolution phase to flag the
175 // need to define the ReturnLabel
177 public void NeedReturnLabel ()
179 if (!HasReturnLabel)
180 HasReturnLabel = true;
183 public Type ReturnType {
184 get { return return_type; }
189 // Expression resolving context
191 public class ResolveContext : IMemberContext
193 [Flags]
194 public enum Options
196 /// <summary>
197 /// This flag tracks the `checked' state of the compilation,
198 /// it controls whether we should generate code that does overflow
199 /// checking, or if we generate code that ignores overflows.
201 /// The default setting comes from the command line option to generate
202 /// checked or unchecked code plus any source code changes using the
203 /// checked/unchecked statements or expressions. Contrast this with
204 /// the ConstantCheckState flag.
205 /// </summary>
206 CheckedScope = 1 << 0,
208 /// <summary>
209 /// The constant check state is always set to `true' and cant be changed
210 /// from the command line. The source code can change this setting with
211 /// the `checked' and `unchecked' statements and expressions.
212 /// </summary>
213 ConstantCheckState = 1 << 1,
215 AllCheckStateFlags = CheckedScope | ConstantCheckState,
218 // unsafe { ... } scope
220 UnsafeScope = 1 << 2,
221 CatchScope = 1 << 3,
222 FinallyScope = 1 << 4,
223 FieldInitializerScope = 1 << 5,
224 CompoundAssignmentScope = 1 << 6,
225 FixedInitializerScope = 1 << 7,
226 BaseInitializer = 1 << 8,
229 // Inside an enum definition, we do not resolve enumeration values
230 // to their enumerations, but rather to the underlying type/value
231 // This is so EnumVal + EnumValB can be evaluated.
233 // There is no "E operator + (E x, E y)", so during an enum evaluation
234 // we relax the rules
236 EnumScope = 1 << 9,
238 ConstantScope = 1 << 10,
240 ConstructorScope = 1 << 11,
242 /// <summary>
243 /// Whether control flow analysis is enabled
244 /// </summary>
245 DoFlowAnalysis = 1 << 20,
247 /// <summary>
248 /// Whether control flow analysis is disabled on structs
249 /// (only meaningful when DoFlowAnalysis is set)
250 /// </summary>
251 OmitStructFlowAnalysis = 1 << 21,
254 /// Indicates the current context is in probing mode, no errors are reported.
256 ProbingMode = 1 << 22,
259 // Return and ContextualReturn statements will set the ReturnType
260 // value based on the expression types of each return statement
261 // instead of the method return type which is initially null.
263 InferReturnType = 1 << 23,
265 OmitDebuggingInfo = 1 << 24,
267 ExpressionTreeConversion = 1 << 25,
269 InvokeSpecialName = 1 << 26
272 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
273 // it's public so that we can use a struct at the callsite
274 public struct FlagsHandle : IDisposable
276 ResolveContext ec;
277 readonly Options invmask, oldval;
279 public FlagsHandle (ResolveContext ec, Options flagsToSet)
280 : this (ec, flagsToSet, flagsToSet)
284 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
286 this.ec = ec;
287 invmask = ~mask;
288 oldval = ec.flags & mask;
289 ec.flags = (ec.flags & invmask) | (val & mask);
291 // if ((mask & Options.ProbingMode) != 0)
292 // ec.Report.DisableReporting ();
295 public void Dispose ()
297 // if ((invmask & Options.ProbingMode) == 0)
298 // ec.Report.EnableReporting ();
300 ec.flags = (ec.flags & invmask) | oldval;
304 Options flags;
307 // Whether we are inside an anonymous method.
309 public AnonymousExpression CurrentAnonymousMethod;
312 // Holds a varible used during collection or object initialization.
314 public Expression CurrentInitializerVariable;
316 public Block CurrentBlock;
318 public IMemberContext MemberContext;
320 /// <summary>
321 /// If this is non-null, points to the current switch statement
322 /// </summary>
323 public Switch Switch;
325 public ResolveContext (IMemberContext mc)
327 if (mc == null)
328 throw new ArgumentNullException ();
330 MemberContext = mc;
333 // The default setting comes from the command line option
335 if (RootContext.Checked)
336 flags |= Options.CheckedScope;
339 // The constant check state is always set to true
341 flags |= Options.ConstantCheckState;
344 public ResolveContext (IMemberContext mc, Options options)
345 : this (mc)
347 flags |= options;
350 public CompilerContext Compiler {
351 get { return MemberContext.Compiler; }
354 public virtual FlowBranching CurrentBranching {
355 get { return null; }
359 // The current iterator
361 public Iterator CurrentIterator {
362 get { return CurrentAnonymousMethod as Iterator; }
365 public Type CurrentType {
366 get { return MemberContext.CurrentType; }
369 public TypeParameter[] CurrentTypeParameters {
370 get { return MemberContext.CurrentTypeParameters; }
373 public TypeContainer CurrentTypeDefinition {
374 get { return MemberContext.CurrentTypeDefinition; }
377 public bool ConstantCheckState {
378 get { return (flags & Options.ConstantCheckState) != 0; }
381 public bool DoFlowAnalysis {
382 get { return (flags & Options.DoFlowAnalysis) != 0; }
385 public bool IsInProbingMode {
386 get { return (flags & Options.ProbingMode) != 0; }
389 public bool IsVariableCapturingRequired {
390 get {
391 return !IsInProbingMode && (CurrentBranching == null || !CurrentBranching.CurrentUsageVector.IsUnreachable);
395 public bool OmitStructFlowAnalysis {
396 get { return (flags & Options.OmitStructFlowAnalysis) != 0; }
399 // TODO: Merge with CompilerGeneratedThis
400 public Expression GetThis (Location loc)
402 This my_this;
403 if (CurrentBlock != null)
404 my_this = new This (CurrentBlock, loc);
405 else
406 my_this = new This (loc);
408 if (!my_this.ResolveBase (this))
409 my_this = null;
411 return my_this;
414 public bool MustCaptureVariable (LocalInfo local)
416 if (CurrentAnonymousMethod == null)
417 return false;
419 // FIXME: IsIterator is too aggressive, we should capture only if child
420 // block contains yield
421 if (CurrentAnonymousMethod.IsIterator)
422 return true;
424 return local.Block.Toplevel != CurrentBlock.Toplevel;
427 public bool HasSet (Options options)
429 return (this.flags & options) == options;
432 public bool HasAny (Options options)
434 return (this.flags & options) != 0;
437 public Report Report {
438 get {
439 return Compiler.Report;
443 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
444 public FlagsHandle Set (Options options)
446 return new FlagsHandle (this, options);
449 public FlagsHandle With (Options options, bool enable)
451 return new FlagsHandle (this, options, enable ? options : 0);
454 #region IMemberContext Members
456 public string GetSignatureForError ()
458 return MemberContext.GetSignatureForError ();
461 public bool IsObsolete {
462 get {
463 // Disables obsolete checks when probing is on
464 return IsInProbingMode || MemberContext.IsObsolete;
468 public bool IsStatic {
469 get { return MemberContext.IsStatic; }
472 public bool IsUnsafe {
473 get { return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe; }
476 public ExtensionMethodGroupExpr LookupExtensionMethod (Type extensionType, string name, Location loc)
478 return MemberContext.LookupExtensionMethod (extensionType, name, loc);
481 public FullNamedExpression LookupNamespaceOrType (string name, Location loc, bool ignore_cs0104)
483 return MemberContext.LookupNamespaceOrType (name, loc, ignore_cs0104);
486 public FullNamedExpression LookupNamespaceAlias (string name)
488 return MemberContext.LookupNamespaceAlias (name);
491 #endregion
495 // This class is used during the Statement.Clone operation
496 // to remap objects that have been cloned.
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.
502 public class CloneContext
504 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
505 Dictionary<LocalInfo, LocalInfo> variable_map;
507 public void AddBlockMap (Block from, Block to)
509 if (block_map.ContainsKey (from))
510 return;
511 block_map[from] = to;
514 public Block LookupBlock (Block from)
516 Block result;
517 if (!block_map.TryGetValue (from, out result)) {
518 result = (Block) from.Clone (this);
519 block_map [from] = result;
522 return result;
526 /// Remaps block to cloned copy if one exists.
528 public Block RemapBlockCopy (Block from)
530 Block mapped_to;
531 if (!block_map.TryGetValue (from, out mapped_to))
532 return from;
534 return mapped_to;
537 public void AddVariableMap (LocalInfo from, LocalInfo to)
539 if (variable_map == null)
540 variable_map = new Dictionary<LocalInfo, LocalInfo> ();
541 else if (variable_map.ContainsKey (from))
542 return;
544 variable_map[from] = to;
547 public LocalInfo LookupVariable (LocalInfo from)
549 try {
550 return variable_map[from];
551 } catch (KeyNotFoundException) {
552 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
558 // Main compiler context
560 public class CompilerContext
562 readonly Report report;
564 public CompilerContext (Report report)
566 this.report = report;
569 public bool IsRuntimeBinder { get; set; }
571 public Report Report {
572 get { return report; }
575 //public PredefinedAttributes PredefinedAttributes {
576 // get { throw new NotImplementedException (); }
581 // Generic code emitter context
583 public class BuilderContext
585 [Flags]
586 public enum Options
588 /// <summary>
589 /// This flag tracks the `checked' state of the compilation,
590 /// it controls whether we should generate code that does overflow
591 /// checking, or if we generate code that ignores overflows.
593 /// The default setting comes from the command line option to generate
594 /// checked or unchecked code plus any source code changes using the
595 /// checked/unchecked statements or expressions. Contrast this with
596 /// the ConstantCheckState flag.
597 /// </summary>
598 CheckedScope = 1 << 0,
600 /// <summary>
601 /// The constant check state is always set to `true' and cant be changed
602 /// from the command line. The source code can change this setting with
603 /// the `checked' and `unchecked' statements and expressions.
604 /// </summary>
605 ConstantCheckState = 1 << 1,
607 AllCheckStateFlags = CheckedScope | ConstantCheckState,
609 OmitDebugInfo = 1 << 2,
611 ConstructorScope = 1 << 3
614 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
615 // it's public so that we can use a struct at the callsite
616 public struct FlagsHandle : IDisposable
618 BuilderContext ec;
619 readonly Options invmask, oldval;
621 public FlagsHandle (BuilderContext ec, Options flagsToSet)
622 : this (ec, flagsToSet, flagsToSet)
626 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
628 this.ec = ec;
629 invmask = ~mask;
630 oldval = ec.flags & mask;
631 ec.flags = (ec.flags & invmask) | (val & mask);
634 public void Dispose ()
636 ec.flags = (ec.flags & invmask) | oldval;
640 Options flags;
642 public bool HasSet (Options options)
644 return (this.flags & options) == options;
647 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
648 public FlagsHandle With (Options options, bool enable)
650 return new FlagsHandle (this, options, enable ? options : 0);