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;
16 using IKVM.Reflection.Emit;
18 using System.Reflection.Emit;
24 // Implemented by elements which can act as independent contexts
25 // during resolve phase. Used mostly for lookups.
27 public interface IMemberContext
30 // A scope type context, it can be inflated for generic types
32 TypeSpec CurrentType { get; }
35 // A scope type parameters either VAR or MVAR
37 TypeParameter[] CurrentTypeParameters { get; }
40 // A member definition of the context. For partial types definition use
41 // CurrentTypeDefinition.PartialContainer otherwise the context is local
43 // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value
45 MemberCore CurrentMemberDefinition { get; }
47 bool IsObsolete { get; }
48 bool IsUnsafe { get; }
49 bool IsStatic { get; }
50 bool HasUnresolvedConstraints { get; }
51 ModuleContainer Module { get; }
53 string GetSignatureForError ();
55 IList<MethodSpec> LookupExtensionMethod (TypeSpec extensionType, string name, int arity, ref NamespaceEntry scope);
56 FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104);
57 FullNamedExpression LookupNamespaceAlias (string name);
59 // TODO: It has been replaced by module
60 CompilerContext Compiler { get; }
64 // Block or statement resolving context
66 public class BlockContext : ResolveContext
68 FlowBranching current_flow_branching;
73 /// The location where return has to jump to return the
76 public Label ReturnLabel; // TODO: It's emit dependant
79 /// If we already defined the ReturnLabel
81 public bool HasReturnLabel;
83 public int FlowOffset;
85 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
88 if (returnType == null)
89 throw new ArgumentNullException ("returnType");
91 this.return_type = returnType;
93 // TODO: check for null value
97 public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
98 : this (rc.MemberContext, block, returnType)
101 flags |= ResolveContext.Options.UnsafeScope;
103 if (rc.HasSet (ResolveContext.Options.CheckedScope))
104 flags |= ResolveContext.Options.CheckedScope;
107 public override FlowBranching CurrentBranching {
108 get { return current_flow_branching; }
112 // Starts a new code branching. This inherits the state of all local
113 // variables and parameters from the current branching.
115 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
117 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
118 return current_flow_branching;
122 // Starts a new code branching for block `block'.
124 public FlowBranching StartFlowBranching (Block block)
126 Set (Options.DoFlowAnalysis);
128 current_flow_branching = FlowBranching.CreateBranching (
129 CurrentBranching, FlowBranching.BranchingType.Block, block, block.StartLocation);
130 return current_flow_branching;
133 public FlowBranchingTryCatch StartFlowBranching (TryCatch stmt)
135 FlowBranchingTryCatch branching = new FlowBranchingTryCatch (CurrentBranching, stmt);
136 current_flow_branching = branching;
140 public FlowBranchingException StartFlowBranching (ExceptionStatement stmt)
142 FlowBranchingException branching = new FlowBranchingException (CurrentBranching, stmt);
143 current_flow_branching = branching;
147 public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt)
149 FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt);
150 current_flow_branching = branching;
154 public FlowBranchingIterator StartFlowBranching (Iterator iterator, FlowBranching parent)
156 FlowBranchingIterator branching = new FlowBranchingIterator (parent, iterator);
157 current_flow_branching = branching;
161 public FlowBranchingToplevel StartFlowBranching (ParametersBlock stmt, FlowBranching parent)
163 FlowBranchingToplevel branching = new FlowBranchingToplevel (parent, stmt);
164 current_flow_branching = branching;
169 // Ends a code branching. Merges the state of locals and parameters
170 // from all the children of the ending branching.
172 public bool EndFlowBranching ()
174 FlowBranching old = current_flow_branching;
175 current_flow_branching = current_flow_branching.Parent;
177 FlowBranching.UsageVector vector = current_flow_branching.MergeChild (old);
178 return vector.IsUnreachable;
182 // Kills the current code branching. This throws away any changed state
183 // information and should only be used in case of an error.
185 // FIXME: this is evil
186 public void KillFlowBranching ()
188 current_flow_branching = current_flow_branching.Parent;
192 // This method is used during the Resolution phase to flag the
193 // need to define the ReturnLabel
195 public void NeedReturnLabel ()
198 HasReturnLabel = true;
201 public TypeSpec ReturnType {
202 get { return return_type; }
207 // Expression resolving context
209 public class ResolveContext : IMemberContext
215 /// This flag tracks the `checked' state of the compilation,
216 /// it controls whether we should generate code that does overflow
217 /// checking, or if we generate code that ignores overflows.
219 /// The default setting comes from the command line option to generate
220 /// checked or unchecked code plus any source code changes using the
221 /// checked/unchecked statements or expressions. Contrast this with
222 /// the ConstantCheckState flag.
224 CheckedScope = 1 << 0,
227 /// The constant check state is always set to `true' and cant be changed
228 /// from the command line. The source code can change this setting with
229 /// the `checked' and `unchecked' statements and expressions.
231 ConstantCheckState = 1 << 1,
233 AllCheckStateFlags = CheckedScope | ConstantCheckState,
236 // unsafe { ... } scope
238 UnsafeScope = 1 << 2,
240 FinallyScope = 1 << 4,
241 FieldInitializerScope = 1 << 5,
242 CompoundAssignmentScope = 1 << 6,
243 FixedInitializerScope = 1 << 7,
244 BaseInitializer = 1 << 8,
247 // Inside an enum definition, we do not resolve enumeration values
248 // to their enumerations, but rather to the underlying type/value
249 // This is so EnumVal + EnumValB can be evaluated.
251 // There is no "E operator + (E x, E y)", so during an enum evaluation
252 // we relax the rules
256 ConstantScope = 1 << 10,
258 ConstructorScope = 1 << 11,
260 UsingInitializerScope = 1 << 12,
263 /// Whether control flow analysis is enabled
265 DoFlowAnalysis = 1 << 20,
268 /// Whether control flow analysis is disabled on structs
269 /// (only meaningful when DoFlowAnalysis is set)
271 OmitStructFlowAnalysis = 1 << 21,
274 /// Indicates the current context is in probing mode, no errors are reported.
276 ProbingMode = 1 << 22,
279 // Return and ContextualReturn statements will set the ReturnType
280 // value based on the expression types of each return statement
281 // instead of the method return type which is initially null.
283 InferReturnType = 1 << 23,
285 OmitDebuggingInfo = 1 << 24,
287 ExpressionTreeConversion = 1 << 25,
289 InvokeSpecialName = 1 << 26
292 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
293 // it's public so that we can use a struct at the callsite
294 public struct FlagsHandle : IDisposable
297 readonly Options invmask, oldval;
299 public FlagsHandle (ResolveContext ec, Options flagsToSet)
300 : this (ec, flagsToSet, flagsToSet)
304 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
308 oldval = ec.flags & mask;
309 ec.flags = (ec.flags & invmask) | (val & mask);
311 // if ((mask & Options.ProbingMode) != 0)
312 // ec.Report.DisableReporting ();
315 public void Dispose ()
317 // if ((invmask & Options.ProbingMode) == 0)
318 // ec.Report.EnableReporting ();
320 ec.flags = (ec.flags & invmask) | oldval;
324 protected Options flags;
327 // Whether we are inside an anonymous method.
329 public AnonymousExpression CurrentAnonymousMethod;
332 // Holds a varible used during collection or object initialization.
334 public Expression CurrentInitializerVariable;
336 public Block CurrentBlock;
338 public readonly IMemberContext MemberContext;
341 /// If this is non-null, points to the current switch statement
343 public Switch Switch;
345 public ResolveContext (IMemberContext mc)
348 throw new ArgumentNullException ();
353 // The default setting comes from the command line option
355 if (RootContext.Checked)
356 flags |= Options.CheckedScope;
359 // The constant check state is always set to true
361 flags |= Options.ConstantCheckState;
364 public ResolveContext (IMemberContext mc, Options options)
370 public CompilerContext Compiler {
371 get { return MemberContext.Compiler; }
374 public virtual ExplicitBlock ConstructorBlock {
376 return CurrentBlock.Explicit;
380 public virtual FlowBranching CurrentBranching {
385 // The current iterator
387 public Iterator CurrentIterator {
388 get { return CurrentAnonymousMethod as Iterator; }
391 public TypeSpec CurrentType {
392 get { return MemberContext.CurrentType; }
395 public TypeParameter[] CurrentTypeParameters {
396 get { return MemberContext.CurrentTypeParameters; }
399 public MemberCore CurrentMemberDefinition {
400 get { return MemberContext.CurrentMemberDefinition; }
403 public bool ConstantCheckState {
404 get { return (flags & Options.ConstantCheckState) != 0; }
407 public bool DoFlowAnalysis {
408 get { return (flags & Options.DoFlowAnalysis) != 0; }
411 public bool HasUnresolvedConstraints {
412 get { return false; }
415 public bool IsInProbingMode {
416 get { return (flags & Options.ProbingMode) != 0; }
419 public bool IsVariableCapturingRequired {
421 return !IsInProbingMode && (CurrentBranching == null || !CurrentBranching.CurrentUsageVector.IsUnreachable);
425 public ModuleContainer Module {
427 return MemberContext.Module;
431 public bool OmitStructFlowAnalysis {
432 get { return (flags & Options.OmitStructFlowAnalysis) != 0; }
435 public bool MustCaptureVariable (INamedBlockVariable local)
437 if (CurrentAnonymousMethod == null)
440 // FIXME: IsIterator is too aggressive, we should capture only if child
441 // block contains yield
442 if (CurrentAnonymousMethod.IsIterator)
445 return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
448 public bool HasSet (Options options)
450 return (this.flags & options) == options;
453 public bool HasAny (Options options)
455 return (this.flags & options) != 0;
458 public Report Report {
460 return Compiler.Report;
464 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
465 public FlagsHandle Set (Options options)
467 return new FlagsHandle (this, options);
470 public FlagsHandle With (Options options, bool enable)
472 return new FlagsHandle (this, options, enable ? options : 0);
475 #region IMemberContext Members
477 public string GetSignatureForError ()
479 return MemberContext.GetSignatureForError ();
482 public bool IsObsolete {
484 // Disables obsolete checks when probing is on
485 return MemberContext.IsObsolete;
489 public bool IsStatic {
490 get { return MemberContext.IsStatic; }
493 public bool IsUnsafe {
494 get { return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe; }
497 public IList<MethodSpec> LookupExtensionMethod (TypeSpec extensionType, string name, int arity, ref NamespaceEntry scope)
499 return MemberContext.LookupExtensionMethod (extensionType, name, arity, ref scope);
502 public FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104)
504 return MemberContext.LookupNamespaceOrType (name, arity, loc, ignore_cs0104);
507 public FullNamedExpression LookupNamespaceAlias (string name)
509 return MemberContext.LookupNamespaceAlias (name);
516 // This class is used during the Statement.Clone operation
517 // to remap objects that have been cloned.
519 // Since blocks are cloned by Block.Clone, we need a way for
520 // expressions that must reference the block to be cloned
521 // pointing to the new cloned block.
523 public class CloneContext
525 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
527 public void AddBlockMap (Block from, Block to)
529 block_map.Add (from, to);
532 public Block LookupBlock (Block from)
535 if (!block_map.TryGetValue (from, out result)) {
536 result = (Block) from.Clone (this);
543 /// Remaps block to cloned copy if one exists.
545 public Block RemapBlockCopy (Block from)
548 if (!block_map.TryGetValue (from, out mapped_to))
556 // Main compiler context
558 public class CompilerContext
560 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
562 readonly Report report;
563 readonly BuildinTypes buildin_types;
565 public CompilerContext (Report report)
567 this.report = report;
568 this.buildin_types = new BuildinTypes ();
569 this.TimeReporter = DisabledTimeReporter;
574 public BuildinTypes BuildinTypes {
576 return buildin_types;
580 public bool IsRuntimeBinder { get; set; }
582 public Report Report {
588 internal TimeReporter TimeReporter { get; set; }
594 // Generic code emitter context
596 public class BuilderContext
602 /// This flag tracks the `checked' state of the compilation,
603 /// it controls whether we should generate code that does overflow
604 /// checking, or if we generate code that ignores overflows.
606 /// The default setting comes from the command line option to generate
607 /// checked or unchecked code plus any source code changes using the
608 /// checked/unchecked statements or expressions. Contrast this with
609 /// the ConstantCheckState flag.
611 CheckedScope = 1 << 0,
614 /// The constant check state is always set to `true' and cant be changed
615 /// from the command line. The source code can change this setting with
616 /// the `checked' and `unchecked' statements and expressions.
618 ConstantCheckState = 1 << 1,
620 AllCheckStateFlags = CheckedScope | ConstantCheckState,
622 OmitDebugInfo = 1 << 2,
624 ConstructorScope = 1 << 3
627 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
628 // it's public so that we can use a struct at the callsite
629 public struct FlagsHandle : IDisposable
632 readonly Options invmask, oldval;
634 public FlagsHandle (BuilderContext ec, Options flagsToSet)
635 : this (ec, flagsToSet, flagsToSet)
639 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
643 oldval = ec.flags & mask;
644 ec.flags = (ec.flags & invmask) | (val & mask);
647 public void Dispose ()
649 ec.flags = (ec.flags & invmask) | oldval;
655 public bool HasSet (Options options)
657 return (this.flags & options) == options;
660 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
661 public FlagsHandle With (Options options, bool enable)
663 return new FlagsHandle (this, options, enable ? options : 0);