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; }
47 string GetSignatureForError ();
49 IList<MethodSpec> LookupExtensionMethod (TypeSpec extensionType, string name, int arity, ref NamespaceEntry scope);
50 FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104);
51 FullNamedExpression LookupNamespaceAlias (string name);
53 CompilerContext Compiler { get; }
57 // Block or statement resolving context
59 public class BlockContext : ResolveContext
61 FlowBranching current_flow_branching;
66 /// The location where return has to jump to return the
69 public Label ReturnLabel; // TODO: It's emit dependant
72 /// If we already defined the ReturnLabel
74 public bool HasReturnLabel;
76 public int FlowOffset;
78 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
81 if (returnType == null)
82 throw new ArgumentNullException ("returnType");
84 this.return_type = returnType;
86 // TODO: check for null value
90 public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
91 : this (rc.MemberContext, block, returnType)
94 flags |= ResolveContext.Options.UnsafeScope;
96 if (rc.HasSet (ResolveContext.Options.CheckedScope))
97 flags |= ResolveContext.Options.CheckedScope;
100 public override FlowBranching CurrentBranching {
101 get { return current_flow_branching; }
105 // Starts a new code branching. This inherits the state of all local
106 // variables and parameters from the current branching.
108 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
110 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
111 return current_flow_branching;
115 // Starts a new code branching for block `block'.
117 public FlowBranching StartFlowBranching (Block block)
119 Set (Options.DoFlowAnalysis);
121 current_flow_branching = FlowBranching.CreateBranching (
122 CurrentBranching, FlowBranching.BranchingType.Block, block, block.StartLocation);
123 return current_flow_branching;
126 public FlowBranchingTryCatch StartFlowBranching (TryCatch stmt)
128 FlowBranchingTryCatch branching = new FlowBranchingTryCatch (CurrentBranching, stmt);
129 current_flow_branching = branching;
133 public FlowBranchingException StartFlowBranching (ExceptionStatement stmt)
135 FlowBranchingException branching = new FlowBranchingException (CurrentBranching, stmt);
136 current_flow_branching = branching;
140 public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt)
142 FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt);
143 current_flow_branching = branching;
147 public FlowBranchingIterator StartFlowBranching (Iterator iterator, FlowBranching parent)
149 FlowBranchingIterator branching = new FlowBranchingIterator (parent, iterator);
150 current_flow_branching = branching;
154 public FlowBranchingToplevel StartFlowBranching (ParametersBlock stmt, FlowBranching parent)
156 FlowBranchingToplevel branching = new FlowBranchingToplevel (parent, stmt);
157 current_flow_branching = branching;
162 // Ends a code branching. Merges the state of locals and parameters
163 // from all the children of the ending branching.
165 public bool EndFlowBranching ()
167 FlowBranching old = current_flow_branching;
168 current_flow_branching = current_flow_branching.Parent;
170 FlowBranching.UsageVector vector = current_flow_branching.MergeChild (old);
171 return vector.IsUnreachable;
175 // Kills the current code branching. This throws away any changed state
176 // information and should only be used in case of an error.
178 // FIXME: this is evil
179 public void KillFlowBranching ()
181 current_flow_branching = current_flow_branching.Parent;
185 // This method is used during the Resolution phase to flag the
186 // need to define the ReturnLabel
188 public void NeedReturnLabel ()
191 HasReturnLabel = true;
194 public TypeSpec ReturnType {
195 get { return return_type; }
200 // Expression resolving context
202 public class ResolveContext : IMemberContext
208 /// This flag tracks the `checked' state of the compilation,
209 /// it controls whether we should generate code that does overflow
210 /// checking, or if we generate code that ignores overflows.
212 /// The default setting comes from the command line option to generate
213 /// checked or unchecked code plus any source code changes using the
214 /// checked/unchecked statements or expressions. Contrast this with
215 /// the ConstantCheckState flag.
217 CheckedScope = 1 << 0,
220 /// The constant check state is always set to `true' and cant be changed
221 /// from the command line. The source code can change this setting with
222 /// the `checked' and `unchecked' statements and expressions.
224 ConstantCheckState = 1 << 1,
226 AllCheckStateFlags = CheckedScope | ConstantCheckState,
229 // unsafe { ... } scope
231 UnsafeScope = 1 << 2,
233 FinallyScope = 1 << 4,
234 FieldInitializerScope = 1 << 5,
235 CompoundAssignmentScope = 1 << 6,
236 FixedInitializerScope = 1 << 7,
237 BaseInitializer = 1 << 8,
240 // Inside an enum definition, we do not resolve enumeration values
241 // to their enumerations, but rather to the underlying type/value
242 // This is so EnumVal + EnumValB can be evaluated.
244 // There is no "E operator + (E x, E y)", so during an enum evaluation
245 // we relax the rules
249 ConstantScope = 1 << 10,
251 ConstructorScope = 1 << 11,
253 UsingInitializerScope = 1 << 12,
256 /// Whether control flow analysis is enabled
258 DoFlowAnalysis = 1 << 20,
261 /// Whether control flow analysis is disabled on structs
262 /// (only meaningful when DoFlowAnalysis is set)
264 OmitStructFlowAnalysis = 1 << 21,
267 /// Indicates the current context is in probing mode, no errors are reported.
269 ProbingMode = 1 << 22,
272 // Return and ContextualReturn statements will set the ReturnType
273 // value based on the expression types of each return statement
274 // instead of the method return type which is initially null.
276 InferReturnType = 1 << 23,
278 OmitDebuggingInfo = 1 << 24,
280 ExpressionTreeConversion = 1 << 25,
282 InvokeSpecialName = 1 << 26
285 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
286 // it's public so that we can use a struct at the callsite
287 public struct FlagsHandle : IDisposable
290 readonly Options invmask, oldval;
292 public FlagsHandle (ResolveContext ec, Options flagsToSet)
293 : this (ec, flagsToSet, flagsToSet)
297 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
301 oldval = ec.flags & mask;
302 ec.flags = (ec.flags & invmask) | (val & mask);
304 // if ((mask & Options.ProbingMode) != 0)
305 // ec.Report.DisableReporting ();
308 public void Dispose ()
310 // if ((invmask & Options.ProbingMode) == 0)
311 // ec.Report.EnableReporting ();
313 ec.flags = (ec.flags & invmask) | oldval;
317 protected Options flags;
320 // Whether we are inside an anonymous method.
322 public AnonymousExpression CurrentAnonymousMethod;
325 // Holds a varible used during collection or object initialization.
327 public Expression CurrentInitializerVariable;
329 public Block CurrentBlock;
331 public IMemberContext MemberContext;
334 /// If this is non-null, points to the current switch statement
336 public Switch Switch;
338 public ResolveContext (IMemberContext mc)
341 throw new ArgumentNullException ();
346 // The default setting comes from the command line option
348 if (RootContext.Checked)
349 flags |= Options.CheckedScope;
352 // The constant check state is always set to true
354 flags |= Options.ConstantCheckState;
357 public ResolveContext (IMemberContext mc, Options options)
363 public CompilerContext Compiler {
364 get { return MemberContext.Compiler; }
367 public virtual ExplicitBlock ConstructorBlock {
369 return CurrentBlock.Explicit;
373 public virtual FlowBranching CurrentBranching {
378 // The current iterator
380 public Iterator CurrentIterator {
381 get { return CurrentAnonymousMethod as Iterator; }
384 public TypeSpec CurrentType {
385 get { return MemberContext.CurrentType; }
388 public TypeParameter[] CurrentTypeParameters {
389 get { return MemberContext.CurrentTypeParameters; }
392 public MemberCore CurrentMemberDefinition {
393 get { return MemberContext.CurrentMemberDefinition; }
396 public bool ConstantCheckState {
397 get { return (flags & Options.ConstantCheckState) != 0; }
400 public bool DoFlowAnalysis {
401 get { return (flags & Options.DoFlowAnalysis) != 0; }
404 public bool HasUnresolvedConstraints {
405 get { return false; }
408 public bool IsInProbingMode {
409 get { return (flags & Options.ProbingMode) != 0; }
412 public bool IsVariableCapturingRequired {
414 return !IsInProbingMode && (CurrentBranching == null || !CurrentBranching.CurrentUsageVector.IsUnreachable);
418 public bool OmitStructFlowAnalysis {
419 get { return (flags & Options.OmitStructFlowAnalysis) != 0; }
422 public bool MustCaptureVariable (INamedBlockVariable local)
424 if (CurrentAnonymousMethod == null)
427 // FIXME: IsIterator is too aggressive, we should capture only if child
428 // block contains yield
429 if (CurrentAnonymousMethod.IsIterator)
432 return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
435 public bool HasSet (Options options)
437 return (this.flags & options) == options;
440 public bool HasAny (Options options)
442 return (this.flags & options) != 0;
445 public Report Report {
447 return Compiler.Report;
451 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
452 public FlagsHandle Set (Options options)
454 return new FlagsHandle (this, options);
457 public FlagsHandle With (Options options, bool enable)
459 return new FlagsHandle (this, options, enable ? options : 0);
462 #region IMemberContext Members
464 public string GetSignatureForError ()
466 return MemberContext.GetSignatureForError ();
469 public bool IsObsolete {
471 // Disables obsolete checks when probing is on
472 return MemberContext.IsObsolete;
476 public bool IsStatic {
477 get { return MemberContext.IsStatic; }
480 public bool IsUnsafe {
481 get { return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe; }
484 public IList<MethodSpec> LookupExtensionMethod (TypeSpec extensionType, string name, int arity, ref NamespaceEntry scope)
486 return MemberContext.LookupExtensionMethod (extensionType, name, arity, ref scope);
489 public FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104)
491 return MemberContext.LookupNamespaceOrType (name, arity, loc, ignore_cs0104);
494 public FullNamedExpression LookupNamespaceAlias (string name)
496 return MemberContext.LookupNamespaceAlias (name);
503 // This class is used during the Statement.Clone operation
504 // to remap objects that have been cloned.
506 // Since blocks are cloned by Block.Clone, we need a way for
507 // expressions that must reference the block to be cloned
508 // pointing to the new cloned block.
510 public class CloneContext
512 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
514 public void AddBlockMap (Block from, Block to)
516 block_map.Add (from, to);
519 public Block LookupBlock (Block from)
522 if (!block_map.TryGetValue (from, out result)) {
523 result = (Block) from.Clone (this);
530 /// Remaps block to cloned copy if one exists.
532 public Block RemapBlockCopy (Block from)
535 if (!block_map.TryGetValue (from, out mapped_to))
543 // Main compiler context
545 public class CompilerContext
547 readonly Report report;
548 readonly ReflectionMetaImporter meta_importer;
549 readonly PredefinedAttributes attributes;
550 readonly GlobalRootNamespace root;
552 public CompilerContext (ReflectionMetaImporter metaImporter, Report report)
554 this.meta_importer = metaImporter;
555 this.report = report;
557 this.attributes = new PredefinedAttributes ();
558 this.root = new GlobalRootNamespace ();
561 public GlobalRootNamespace GlobalRootNamespace {
567 public bool IsRuntimeBinder { get; set; }
569 public ReflectionMetaImporter MetaImporter {
571 return meta_importer;
575 public PredefinedAttributes PredefinedAttributes {
581 public Report Report {
589 // Generic code emitter context
591 public class BuilderContext
597 /// This flag tracks the `checked' state of the compilation,
598 /// it controls whether we should generate code that does overflow
599 /// checking, or if we generate code that ignores overflows.
601 /// The default setting comes from the command line option to generate
602 /// checked or unchecked code plus any source code changes using the
603 /// checked/unchecked statements or expressions. Contrast this with
604 /// the ConstantCheckState flag.
606 CheckedScope = 1 << 0,
609 /// The constant check state is always set to `true' and cant be changed
610 /// from the command line. The source code can change this setting with
611 /// the `checked' and `unchecked' statements and expressions.
613 ConstantCheckState = 1 << 1,
615 AllCheckStateFlags = CheckedScope | ConstantCheckState,
617 OmitDebugInfo = 1 << 2,
619 ConstructorScope = 1 << 3
622 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
623 // it's public so that we can use a struct at the callsite
624 public struct FlagsHandle : IDisposable
627 readonly Options invmask, oldval;
629 public FlagsHandle (BuilderContext ec, Options flagsToSet)
630 : this (ec, flagsToSet, flagsToSet)
634 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
638 oldval = ec.flags & mask;
639 ec.flags = (ec.flags & invmask) | (val & mask);
642 public void Dispose ()
644 ec.flags = (ec.flags & invmask) | oldval;
650 public bool HasSet (Options options)
652 return (this.flags & options) == options;
655 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
656 public FlagsHandle With (Options options, bool enable)
658 return new FlagsHandle (this, options, enable ? options : 0);