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 MemberCore CurrentMemberDefinition { get; }
40 bool IsObsolete { get; }
41 bool IsUnsafe { get; }
42 bool IsStatic { get; }
43 bool HasUnresolvedConstraints { get; }
45 string GetSignatureForError ();
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);
51 CompilerContext Compiler { get; }
55 // Block or statement resolving context
57 public class BlockContext : ResolveContext
59 FlowBranching current_flow_branching;
64 /// The location where return has to jump to return the
67 public Label ReturnLabel; // TODO: It's emit dependant
70 /// If we already defined the ReturnLabel
72 public bool HasReturnLabel;
74 public int FlowOffset;
76 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
79 if (returnType == null)
80 throw new ArgumentNullException ("returnType");
82 this.return_type = returnType;
84 // TODO: check for null value
88 public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
89 : this (rc.MemberContext, block, returnType)
92 flags |= ResolveContext.Options.UnsafeScope;
94 if (rc.HasSet (ResolveContext.Options.CheckedScope))
95 flags |= ResolveContext.Options.CheckedScope;
98 public override FlowBranching CurrentBranching {
99 get { return current_flow_branching; }
103 // Starts a new code branching. This inherits the state of all local
104 // variables and parameters from the current branching.
106 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
108 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
109 return current_flow_branching;
113 // Starts a new code branching for block `block'.
115 public FlowBranching StartFlowBranching (Block block)
117 Set (Options.DoFlowAnalysis);
119 current_flow_branching = FlowBranching.CreateBranching (
120 CurrentBranching, FlowBranching.BranchingType.Block, block, block.StartLocation);
121 return current_flow_branching;
124 public FlowBranchingTryCatch StartFlowBranching (TryCatch stmt)
126 FlowBranchingTryCatch branching = new FlowBranchingTryCatch (CurrentBranching, stmt);
127 current_flow_branching = branching;
131 public FlowBranchingException StartFlowBranching (ExceptionStatement stmt)
133 FlowBranchingException branching = new FlowBranchingException (CurrentBranching, stmt);
134 current_flow_branching = branching;
138 public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt)
140 FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt);
141 current_flow_branching = branching;
145 public FlowBranchingIterator StartFlowBranching (Iterator iterator, FlowBranching parent)
147 FlowBranchingIterator branching = new FlowBranchingIterator (parent, iterator);
148 current_flow_branching = branching;
152 public FlowBranchingToplevel StartFlowBranching (ParametersBlock stmt, FlowBranching parent)
154 FlowBranchingToplevel branching = new FlowBranchingToplevel (parent, stmt);
155 current_flow_branching = branching;
160 // Ends a code branching. Merges the state of locals and parameters
161 // from all the children of the ending branching.
163 public bool EndFlowBranching ()
165 FlowBranching old = current_flow_branching;
166 current_flow_branching = current_flow_branching.Parent;
168 FlowBranching.UsageVector vector = current_flow_branching.MergeChild (old);
169 return vector.IsUnreachable;
173 // Kills the current code branching. This throws away any changed state
174 // information and should only be used in case of an error.
176 // FIXME: this is evil
177 public void KillFlowBranching ()
179 current_flow_branching = current_flow_branching.Parent;
183 // This method is used during the Resolution phase to flag the
184 // need to define the ReturnLabel
186 public void NeedReturnLabel ()
189 HasReturnLabel = true;
192 public TypeSpec ReturnType {
193 get { return return_type; }
198 // Expression resolving context
200 public class ResolveContext : IMemberContext
206 /// This flag tracks the `checked' state of the compilation,
207 /// it controls whether we should generate code that does overflow
208 /// checking, or if we generate code that ignores overflows.
210 /// The default setting comes from the command line option to generate
211 /// checked or unchecked code plus any source code changes using the
212 /// checked/unchecked statements or expressions. Contrast this with
213 /// the ConstantCheckState flag.
215 CheckedScope = 1 << 0,
218 /// The constant check state is always set to `true' and cant be changed
219 /// from the command line. The source code can change this setting with
220 /// the `checked' and `unchecked' statements and expressions.
222 ConstantCheckState = 1 << 1,
224 AllCheckStateFlags = CheckedScope | ConstantCheckState,
227 // unsafe { ... } scope
229 UnsafeScope = 1 << 2,
231 FinallyScope = 1 << 4,
232 FieldInitializerScope = 1 << 5,
233 CompoundAssignmentScope = 1 << 6,
234 FixedInitializerScope = 1 << 7,
235 BaseInitializer = 1 << 8,
238 // Inside an enum definition, we do not resolve enumeration values
239 // to their enumerations, but rather to the underlying type/value
240 // This is so EnumVal + EnumValB can be evaluated.
242 // There is no "E operator + (E x, E y)", so during an enum evaluation
243 // we relax the rules
247 ConstantScope = 1 << 10,
249 ConstructorScope = 1 << 11,
251 UsingInitializerScope = 1 << 12,
254 /// Whether control flow analysis is enabled
256 DoFlowAnalysis = 1 << 20,
259 /// Whether control flow analysis is disabled on structs
260 /// (only meaningful when DoFlowAnalysis is set)
262 OmitStructFlowAnalysis = 1 << 21,
265 /// Indicates the current context is in probing mode, no errors are reported.
267 ProbingMode = 1 << 22,
270 // Return and ContextualReturn statements will set the ReturnType
271 // value based on the expression types of each return statement
272 // instead of the method return type which is initially null.
274 InferReturnType = 1 << 23,
276 OmitDebuggingInfo = 1 << 24,
278 ExpressionTreeConversion = 1 << 25,
280 InvokeSpecialName = 1 << 26
283 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
284 // it's public so that we can use a struct at the callsite
285 public struct FlagsHandle : IDisposable
288 readonly Options invmask, oldval;
290 public FlagsHandle (ResolveContext ec, Options flagsToSet)
291 : this (ec, flagsToSet, flagsToSet)
295 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
299 oldval = ec.flags & mask;
300 ec.flags = (ec.flags & invmask) | (val & mask);
302 // if ((mask & Options.ProbingMode) != 0)
303 // ec.Report.DisableReporting ();
306 public void Dispose ()
308 // if ((invmask & Options.ProbingMode) == 0)
309 // ec.Report.EnableReporting ();
311 ec.flags = (ec.flags & invmask) | oldval;
315 protected Options flags;
318 // Whether we are inside an anonymous method.
320 public AnonymousExpression CurrentAnonymousMethod;
323 // Holds a varible used during collection or object initialization.
325 public Expression CurrentInitializerVariable;
327 public Block CurrentBlock;
329 public IMemberContext MemberContext;
332 /// If this is non-null, points to the current switch statement
334 public Switch Switch;
336 public ResolveContext (IMemberContext mc)
339 throw new ArgumentNullException ();
344 // The default setting comes from the command line option
346 if (RootContext.Checked)
347 flags |= Options.CheckedScope;
350 // The constant check state is always set to true
352 flags |= Options.ConstantCheckState;
355 public ResolveContext (IMemberContext mc, Options options)
361 public CompilerContext Compiler {
362 get { return MemberContext.Compiler; }
365 public virtual ExplicitBlock ConstructorBlock {
367 return CurrentBlock.Explicit;
371 public virtual FlowBranching CurrentBranching {
376 // The current iterator
378 public Iterator CurrentIterator {
379 get { return CurrentAnonymousMethod as Iterator; }
382 public TypeSpec CurrentType {
383 get { return MemberContext.CurrentType; }
386 public TypeParameter[] CurrentTypeParameters {
387 get { return MemberContext.CurrentTypeParameters; }
390 public MemberCore CurrentMemberDefinition {
391 get { return MemberContext.CurrentMemberDefinition; }
394 public bool ConstantCheckState {
395 get { return (flags & Options.ConstantCheckState) != 0; }
398 public bool DoFlowAnalysis {
399 get { return (flags & Options.DoFlowAnalysis) != 0; }
402 public bool HasUnresolvedConstraints {
403 get { return false; }
406 public bool IsInProbingMode {
407 get { return (flags & Options.ProbingMode) != 0; }
410 public bool IsVariableCapturingRequired {
412 return !IsInProbingMode && (CurrentBranching == null || !CurrentBranching.CurrentUsageVector.IsUnreachable);
416 public bool OmitStructFlowAnalysis {
417 get { return (flags & Options.OmitStructFlowAnalysis) != 0; }
420 public bool MustCaptureVariable (INamedBlockVariable local)
422 if (CurrentAnonymousMethod == null)
425 // FIXME: IsIterator is too aggressive, we should capture only if child
426 // block contains yield
427 if (CurrentAnonymousMethod.IsIterator)
430 return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
433 public bool HasSet (Options options)
435 return (this.flags & options) == options;
438 public bool HasAny (Options options)
440 return (this.flags & options) != 0;
443 public Report Report {
445 return Compiler.Report;
449 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
450 public FlagsHandle Set (Options options)
452 return new FlagsHandle (this, options);
455 public FlagsHandle With (Options options, bool enable)
457 return new FlagsHandle (this, options, enable ? options : 0);
460 #region IMemberContext Members
462 public string GetSignatureForError ()
464 return MemberContext.GetSignatureForError ();
467 public bool IsObsolete {
469 // Disables obsolete checks when probing is on
470 return MemberContext.IsObsolete;
474 public bool IsStatic {
475 get { return MemberContext.IsStatic; }
478 public bool IsUnsafe {
479 get { return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe; }
482 public IList<MethodSpec> LookupExtensionMethod (TypeSpec extensionType, string name, int arity, ref NamespaceEntry scope)
484 return MemberContext.LookupExtensionMethod (extensionType, name, arity, ref scope);
487 public FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104)
489 return MemberContext.LookupNamespaceOrType (name, arity, loc, ignore_cs0104);
492 public FullNamedExpression LookupNamespaceAlias (string name)
494 return MemberContext.LookupNamespaceAlias (name);
501 // This class is used during the Statement.Clone operation
502 // to remap objects that have been cloned.
504 // Since blocks are cloned by Block.Clone, we need a way for
505 // expressions that must reference the block to be cloned
506 // pointing to the new cloned block.
508 public class CloneContext
510 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
512 public void AddBlockMap (Block from, Block to)
514 block_map.Add (from, to);
517 public Block LookupBlock (Block from)
520 if (!block_map.TryGetValue (from, out result)) {
521 result = (Block) from.Clone (this);
528 /// Remaps block to cloned copy if one exists.
530 public Block RemapBlockCopy (Block from)
533 if (!block_map.TryGetValue (from, out mapped_to))
541 // Main compiler context
543 public class CompilerContext
545 readonly Report report;
546 readonly ReflectionMetaImporter meta_importer;
547 readonly PredefinedAttributes attributes;
548 readonly GlobalRootNamespace root;
550 public CompilerContext (ReflectionMetaImporter metaImporter, Report report)
552 this.meta_importer = metaImporter;
553 this.report = report;
555 this.attributes = new PredefinedAttributes ();
556 this.root = new GlobalRootNamespace ();
559 public GlobalRootNamespace GlobalRootNamespace {
565 public bool IsRuntimeBinder { get; set; }
567 public ReflectionMetaImporter MetaImporter {
569 return meta_importer;
573 public PredefinedAttributes PredefinedAttributes {
579 public Report Report {
587 // Generic code emitter context
589 public class BuilderContext
595 /// This flag tracks the `checked' state of the compilation,
596 /// it controls whether we should generate code that does overflow
597 /// checking, or if we generate code that ignores overflows.
599 /// The default setting comes from the command line option to generate
600 /// checked or unchecked code plus any source code changes using the
601 /// checked/unchecked statements or expressions. Contrast this with
602 /// the ConstantCheckState flag.
604 CheckedScope = 1 << 0,
607 /// The constant check state is always set to `true' and cant be changed
608 /// from the command line. The source code can change this setting with
609 /// the `checked' and `unchecked' statements and expressions.
611 ConstantCheckState = 1 << 1,
613 AllCheckStateFlags = CheckedScope | ConstantCheckState,
615 OmitDebugInfo = 1 << 2,
617 ConstructorScope = 1 << 3
620 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
621 // it's public so that we can use a struct at the callsite
622 public struct FlagsHandle : IDisposable
625 readonly Options invmask, oldval;
627 public FlagsHandle (BuilderContext ec, Options flagsToSet)
628 : this (ec, flagsToSet, flagsToSet)
632 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
636 oldval = ec.flags & mask;
637 ec.flags = (ec.flags & invmask) | (val & mask);
640 public void Dispose ()
642 ec.flags = (ec.flags & invmask) | oldval;
648 public bool HasSet (Options options)
650 return (this.flags & options) == options;
653 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
654 public FlagsHandle With (Options options, bool enable)
656 return new FlagsHandle (this, options, enable ? options : 0);