2 // context.cs: Various compiler contexts.
5 // Marek Safar (marek.safar@gmail.com)
6 // Miguel de Icaza (miguel@ximian.com)
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2004-2009 Novell, Inc.
13 using System.Collections.Generic;
14 using System.Reflection.Emit;
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 // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value
40 MemberCore CurrentMemberDefinition { get; }
42 bool IsObsolete { get; }
43 bool IsUnsafe { get; }
44 bool IsStatic { get; }
45 bool HasUnresolvedConstraints { get; }
46 ModuleContainer Module { get; }
48 string GetSignatureForError ();
50 IList<MethodSpec> LookupExtensionMethod (TypeSpec extensionType, string name, int arity, ref NamespaceEntry scope);
51 FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104);
52 FullNamedExpression LookupNamespaceAlias (string name);
54 // TODO: It has been replaced by module
55 CompilerContext Compiler { get; }
59 // Block or statement resolving context
61 public class BlockContext : ResolveContext
63 FlowBranching current_flow_branching;
68 /// The location where return has to jump to return the
71 public Label ReturnLabel; // TODO: It's emit dependant
74 /// If we already defined the ReturnLabel
76 public bool HasReturnLabel;
78 public int FlowOffset;
80 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
83 if (returnType == null)
84 throw new ArgumentNullException ("returnType");
86 this.return_type = returnType;
88 // TODO: check for null value
92 public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
93 : this (rc.MemberContext, block, returnType)
96 flags |= ResolveContext.Options.UnsafeScope;
98 if (rc.HasSet (ResolveContext.Options.CheckedScope))
99 flags |= ResolveContext.Options.CheckedScope;
102 public override FlowBranching CurrentBranching {
103 get { return current_flow_branching; }
107 // Starts a new code branching. This inherits the state of all local
108 // variables and parameters from the current branching.
110 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
112 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
113 return current_flow_branching;
117 // Starts a new code branching for block `block'.
119 public FlowBranching StartFlowBranching (Block block)
121 Set (Options.DoFlowAnalysis);
123 current_flow_branching = FlowBranching.CreateBranching (
124 CurrentBranching, FlowBranching.BranchingType.Block, block, block.StartLocation);
125 return current_flow_branching;
128 public FlowBranchingTryCatch StartFlowBranching (TryCatch stmt)
130 FlowBranchingTryCatch branching = new FlowBranchingTryCatch (CurrentBranching, stmt);
131 current_flow_branching = branching;
135 public FlowBranchingException StartFlowBranching (ExceptionStatement stmt)
137 FlowBranchingException branching = new FlowBranchingException (CurrentBranching, stmt);
138 current_flow_branching = branching;
142 public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt)
144 FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt);
145 current_flow_branching = branching;
149 public FlowBranchingIterator StartFlowBranching (Iterator iterator, FlowBranching parent)
151 FlowBranchingIterator branching = new FlowBranchingIterator (parent, iterator);
152 current_flow_branching = branching;
156 public FlowBranchingToplevel StartFlowBranching (ParametersBlock stmt, FlowBranching parent)
158 FlowBranchingToplevel branching = new FlowBranchingToplevel (parent, stmt);
159 current_flow_branching = branching;
164 // Ends a code branching. Merges the state of locals and parameters
165 // from all the children of the ending branching.
167 public bool EndFlowBranching ()
169 FlowBranching old = current_flow_branching;
170 current_flow_branching = current_flow_branching.Parent;
172 FlowBranching.UsageVector vector = current_flow_branching.MergeChild (old);
173 return vector.IsUnreachable;
177 // Kills the current code branching. This throws away any changed state
178 // information and should only be used in case of an error.
180 // FIXME: this is evil
181 public void KillFlowBranching ()
183 current_flow_branching = current_flow_branching.Parent;
187 // This method is used during the Resolution phase to flag the
188 // need to define the ReturnLabel
190 public void NeedReturnLabel ()
193 HasReturnLabel = true;
196 public TypeSpec ReturnType {
197 get { return return_type; }
202 // Expression resolving context
204 public class ResolveContext : IMemberContext
210 /// This flag tracks the `checked' state of the compilation,
211 /// it controls whether we should generate code that does overflow
212 /// checking, or if we generate code that ignores overflows.
214 /// The default setting comes from the command line option to generate
215 /// checked or unchecked code plus any source code changes using the
216 /// checked/unchecked statements or expressions. Contrast this with
217 /// the ConstantCheckState flag.
219 CheckedScope = 1 << 0,
222 /// The constant check state is always set to `true' and cant be changed
223 /// from the command line. The source code can change this setting with
224 /// the `checked' and `unchecked' statements and expressions.
226 ConstantCheckState = 1 << 1,
228 AllCheckStateFlags = CheckedScope | ConstantCheckState,
231 // unsafe { ... } scope
233 UnsafeScope = 1 << 2,
235 FinallyScope = 1 << 4,
236 FieldInitializerScope = 1 << 5,
237 CompoundAssignmentScope = 1 << 6,
238 FixedInitializerScope = 1 << 7,
239 BaseInitializer = 1 << 8,
242 // Inside an enum definition, we do not resolve enumeration values
243 // to their enumerations, but rather to the underlying type/value
244 // This is so EnumVal + EnumValB can be evaluated.
246 // There is no "E operator + (E x, E y)", so during an enum evaluation
247 // we relax the rules
251 ConstantScope = 1 << 10,
253 ConstructorScope = 1 << 11,
255 UsingInitializerScope = 1 << 12,
258 /// Whether control flow analysis is enabled
260 DoFlowAnalysis = 1 << 20,
263 /// Whether control flow analysis is disabled on structs
264 /// (only meaningful when DoFlowAnalysis is set)
266 OmitStructFlowAnalysis = 1 << 21,
269 /// Indicates the current context is in probing mode, no errors are reported.
271 ProbingMode = 1 << 22,
274 // Return and ContextualReturn statements will set the ReturnType
275 // value based on the expression types of each return statement
276 // instead of the method return type which is initially null.
278 InferReturnType = 1 << 23,
280 OmitDebuggingInfo = 1 << 24,
282 ExpressionTreeConversion = 1 << 25,
284 InvokeSpecialName = 1 << 26
287 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
288 // it's public so that we can use a struct at the callsite
289 public struct FlagsHandle : IDisposable
292 readonly Options invmask, oldval;
294 public FlagsHandle (ResolveContext ec, Options flagsToSet)
295 : this (ec, flagsToSet, flagsToSet)
299 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
303 oldval = ec.flags & mask;
304 ec.flags = (ec.flags & invmask) | (val & mask);
306 // if ((mask & Options.ProbingMode) != 0)
307 // ec.Report.DisableReporting ();
310 public void Dispose ()
312 // if ((invmask & Options.ProbingMode) == 0)
313 // ec.Report.EnableReporting ();
315 ec.flags = (ec.flags & invmask) | oldval;
319 protected Options flags;
322 // Whether we are inside an anonymous method.
324 public AnonymousExpression CurrentAnonymousMethod;
327 // Holds a varible used during collection or object initialization.
329 public Expression CurrentInitializerVariable;
331 public Block CurrentBlock;
333 public readonly IMemberContext MemberContext;
336 /// If this is non-null, points to the current switch statement
338 public Switch Switch;
340 public ResolveContext (IMemberContext mc)
343 throw new ArgumentNullException ();
348 // The default setting comes from the command line option
350 if (RootContext.Checked)
351 flags |= Options.CheckedScope;
354 // The constant check state is always set to true
356 flags |= Options.ConstantCheckState;
359 public ResolveContext (IMemberContext mc, Options options)
365 public CompilerContext Compiler {
366 get { return MemberContext.Compiler; }
369 public virtual ExplicitBlock ConstructorBlock {
371 return CurrentBlock.Explicit;
375 public virtual FlowBranching CurrentBranching {
380 // The current iterator
382 public Iterator CurrentIterator {
383 get { return CurrentAnonymousMethod as Iterator; }
386 public TypeSpec CurrentType {
387 get { return MemberContext.CurrentType; }
390 public TypeParameter[] CurrentTypeParameters {
391 get { return MemberContext.CurrentTypeParameters; }
394 public MemberCore CurrentMemberDefinition {
395 get { return MemberContext.CurrentMemberDefinition; }
398 public bool ConstantCheckState {
399 get { return (flags & Options.ConstantCheckState) != 0; }
402 public bool DoFlowAnalysis {
403 get { return (flags & Options.DoFlowAnalysis) != 0; }
406 public bool HasUnresolvedConstraints {
407 get { return false; }
410 public bool IsInProbingMode {
411 get { return (flags & Options.ProbingMode) != 0; }
414 public bool IsVariableCapturingRequired {
416 return !IsInProbingMode && (CurrentBranching == null || !CurrentBranching.CurrentUsageVector.IsUnreachable);
420 public ModuleContainer Module {
422 return MemberContext.Module;
426 public bool OmitStructFlowAnalysis {
427 get { return (flags & Options.OmitStructFlowAnalysis) != 0; }
430 public bool MustCaptureVariable (INamedBlockVariable local)
432 if (CurrentAnonymousMethod == null)
435 // FIXME: IsIterator is too aggressive, we should capture only if child
436 // block contains yield
437 if (CurrentAnonymousMethod.IsIterator)
440 return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
443 public bool HasSet (Options options)
445 return (this.flags & options) == options;
448 public bool HasAny (Options options)
450 return (this.flags & options) != 0;
453 public Report Report {
455 return Compiler.Report;
459 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
460 public FlagsHandle Set (Options options)
462 return new FlagsHandle (this, options);
465 public FlagsHandle With (Options options, bool enable)
467 return new FlagsHandle (this, options, enable ? options : 0);
470 #region IMemberContext Members
472 public string GetSignatureForError ()
474 return MemberContext.GetSignatureForError ();
477 public bool IsObsolete {
479 // Disables obsolete checks when probing is on
480 return MemberContext.IsObsolete;
484 public bool IsStatic {
485 get { return MemberContext.IsStatic; }
488 public bool IsUnsafe {
489 get { return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe; }
492 public IList<MethodSpec> LookupExtensionMethod (TypeSpec extensionType, string name, int arity, ref NamespaceEntry scope)
494 return MemberContext.LookupExtensionMethod (extensionType, name, arity, ref scope);
497 public FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104)
499 return MemberContext.LookupNamespaceOrType (name, arity, loc, ignore_cs0104);
502 public FullNamedExpression LookupNamespaceAlias (string name)
504 return MemberContext.LookupNamespaceAlias (name);
511 // This class is used during the Statement.Clone operation
512 // to remap objects that have been cloned.
514 // Since blocks are cloned by Block.Clone, we need a way for
515 // expressions that must reference the block to be cloned
516 // pointing to the new cloned block.
518 public class CloneContext
520 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
522 public void AddBlockMap (Block from, Block to)
524 block_map.Add (from, to);
527 public Block LookupBlock (Block from)
530 if (!block_map.TryGetValue (from, out result)) {
531 result = (Block) from.Clone (this);
538 /// Remaps block to cloned copy if one exists.
540 public Block RemapBlockCopy (Block from)
543 if (!block_map.TryGetValue (from, out mapped_to))
551 // Main compiler context
553 public class CompilerContext
555 readonly Report report;
556 readonly BuildinTypes buildin_types;
558 public CompilerContext (Report report)
560 this.report = report;
561 this.buildin_types = new BuildinTypes ();
566 public BuildinTypes BuildinTypes {
568 return buildin_types;
572 public bool IsRuntimeBinder { get; set; }
574 public Report Report {
584 // Generic code emitter context
586 public class BuilderContext
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.
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.
601 CheckedScope = 1 << 0,
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.
608 ConstantCheckState = 1 << 1,
610 AllCheckStateFlags = CheckedScope | ConstantCheckState,
612 OmitDebugInfo = 1 << 2,
614 ConstructorScope = 1 << 3
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
622 readonly Options invmask, oldval;
624 public FlagsHandle (BuilderContext ec, Options flagsToSet)
625 : this (ec, flagsToSet, flagsToSet)
629 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
633 oldval = ec.flags & mask;
634 ec.flags = (ec.flags & invmask) | (val & mask);
637 public void Dispose ()
639 ec.flags = (ec.flags & invmask) | oldval;
645 public bool HasSet (Options options)
647 return (this.flags & options) == options;
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)
653 return new FlagsHandle (this, options, enable ? options : 0);