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.
10 // Copyright 2011 Xamarin Inc.
14 using System.Collections.Generic;
16 using System.Security.Cryptography;
17 using System.Diagnostics;
21 public enum LookupMode
25 IgnoreAccessibility = 2
29 // Implemented by elements which can act as independent contexts
30 // during resolve phase. Used mostly for lookups.
32 public interface IMemberContext : IModuleContext
35 // A scope type context, it can be inflated for generic types
37 TypeSpec CurrentType { get; }
40 // A scope type parameters either VAR or MVAR
42 TypeParameters CurrentTypeParameters { get; }
45 // A member definition of the context. For partial types definition use
46 // CurrentTypeDefinition.PartialContainer otherwise the context is local
48 // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value
50 MemberCore CurrentMemberDefinition { get; }
52 bool IsObsolete { get; }
53 bool IsUnsafe { get; }
54 bool IsStatic { get; }
56 string GetSignatureForError ();
58 ExtensionMethodCandidates LookupExtensionMethod (string name, int arity);
59 FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc);
60 FullNamedExpression LookupNamespaceAlias (string name);
63 public interface IModuleContext
65 ModuleContainer Module { get; }
69 // Block or statement resolving context
71 public class BlockContext : ResolveContext
73 readonly TypeSpec return_type;
76 // Tracks the last offset used by VariableInfo
78 public int AssignmentInfoOffset;
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;
101 if (!rc.ConstantCheckState)
102 flags &= ~Options.ConstantCheckState;
104 if (rc.IsInProbingMode)
105 flags |= ResolveContext.Options.ProbingMode;
107 if (rc.HasSet (ResolveContext.Options.FieldInitializerScope))
108 flags |= ResolveContext.Options.FieldInitializerScope;
110 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
111 flags |= ResolveContext.Options.ExpressionTreeConversion;
113 if (rc.HasSet (ResolveContext.Options.BaseInitializer))
114 flags |= ResolveContext.Options.BaseInitializer;
117 public ExceptionStatement CurrentTryBlock { get; set; }
119 public LoopStatement EnclosingLoop { get; set; }
121 public LoopStatement EnclosingLoopOrSwitch { get; set; }
123 public Switch Switch { get; set; }
125 public TypeSpec ReturnType {
126 get { return return_type; }
131 // Expression resolving context
133 public class ResolveContext : IMemberContext
139 /// This flag tracks the `checked' state of the compilation,
140 /// it controls whether we should generate code that does overflow
141 /// checking, or if we generate code that ignores overflows.
143 /// The default setting comes from the command line option to generate
144 /// checked or unchecked code plus any source code changes using the
145 /// checked/unchecked statements or expressions. Contrast this with
146 /// the ConstantCheckState flag.
148 CheckedScope = 1 << 0,
151 /// The constant check state is always set to `true' and cant be changed
152 /// from the command line. The source code can change this setting with
153 /// the `checked' and `unchecked' statements and expressions.
155 ConstantCheckState = 1 << 1,
157 AllCheckStateFlags = CheckedScope | ConstantCheckState,
160 // unsafe { ... } scope
162 UnsafeScope = 1 << 2,
164 FinallyScope = 1 << 4,
165 FieldInitializerScope = 1 << 5,
166 CompoundAssignmentScope = 1 << 6,
167 FixedInitializerScope = 1 << 7,
168 BaseInitializer = 1 << 8,
171 // Inside an enum definition, we do not resolve enumeration values
172 // to their enumerations, but rather to the underlying type/value
173 // This is so EnumVal + EnumValB can be evaluated.
175 // There is no "E operator + (E x, E y)", so during an enum evaluation
176 // we relax the rules
180 ConstantScope = 1 << 10,
182 ConstructorScope = 1 << 11,
184 UsingInitializerScope = 1 << 12,
190 TryWithCatchScope = 1 << 15,
192 DontSetConditionalAccessReceiver = 1 << 16,
194 NameOfScope = 1 << 17,
197 /// Indicates the current context is in probing mode, no errors are reported.
199 ProbingMode = 1 << 22,
202 // Return and ContextualReturn statements will set the ReturnType
203 // value based on the expression types of each return statement
204 // instead of the method return type which is initially null.
206 InferReturnType = 1 << 23,
208 OmitDebuggingInfo = 1 << 24,
210 ExpressionTreeConversion = 1 << 25,
212 InvokeSpecialName = 1 << 26
215 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
216 // it's public so that we can use a struct at the callsite
217 public struct FlagsHandle : IDisposable
219 readonly ResolveContext ec;
220 readonly Options invmask, oldval;
222 public FlagsHandle (ResolveContext ec, Options flagsToSet)
223 : this (ec, flagsToSet, flagsToSet)
227 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
231 oldval = ec.flags & mask;
232 ec.flags = (ec.flags & invmask) | (val & mask);
234 // if ((mask & Options.ProbingMode) != 0)
235 // ec.Report.DisableReporting ();
238 public void Dispose ()
240 // if ((invmask & Options.ProbingMode) == 0)
241 // ec.Report.EnableReporting ();
243 ec.flags = (ec.flags & invmask) | oldval;
247 protected Options flags;
250 // Whether we are inside an anonymous method.
252 public AnonymousExpression CurrentAnonymousMethod;
255 // Holds a varible used during collection or object initialization.
257 public Expression CurrentInitializerVariable;
259 public Block CurrentBlock;
261 public readonly IMemberContext MemberContext;
263 public ResolveContext (IMemberContext mc)
266 throw new ArgumentNullException ();
271 // The default setting comes from the command line option
273 if (mc.Module.Compiler.Settings.Checked)
274 flags |= Options.CheckedScope;
277 // The constant check state is always set to true
279 flags |= Options.ConstantCheckState;
282 public ResolveContext (IMemberContext mc, Options options)
290 public BuiltinTypes BuiltinTypes {
292 return MemberContext.Module.Compiler.BuiltinTypes;
296 public virtual ExplicitBlock ConstructorBlock {
298 return CurrentBlock.Explicit;
303 // The current iterator
305 public Iterator CurrentIterator {
306 get { return CurrentAnonymousMethod as Iterator; }
309 public TypeSpec CurrentType {
310 get { return MemberContext.CurrentType; }
313 public TypeParameters CurrentTypeParameters {
314 get { return MemberContext.CurrentTypeParameters; }
317 public MemberCore CurrentMemberDefinition {
318 get { return MemberContext.CurrentMemberDefinition; }
321 public bool ConstantCheckState {
322 get { return (flags & Options.ConstantCheckState) != 0; }
325 public bool IsInProbingMode {
327 return (flags & Options.ProbingMode) != 0;
331 public bool IsObsolete {
333 // Disables obsolete checks when probing is on
334 return MemberContext.IsObsolete;
338 public bool IsStatic {
340 return MemberContext.IsStatic;
344 public bool IsUnsafe {
346 return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
350 public bool IsRuntimeBinder {
352 return Module.Compiler.IsRuntimeBinder;
356 public bool IsVariableCapturingRequired {
358 return !IsInProbingMode;
362 public ModuleContainer Module {
364 return MemberContext.Module;
368 public Report Report {
370 return Module.Compiler.Report;
376 public bool MustCaptureVariable (INamedBlockVariable local)
378 if (CurrentAnonymousMethod == null)
382 // Capture only if this or any of child blocks contain yield
383 // or it's a parameter
385 if (CurrentAnonymousMethod.IsIterator)
386 return local.IsParameter || local.Block.Explicit.HasYield;
389 // Capture only if this or any of child blocks contain await
390 // or it's a parameter or we need to access variable from
391 // different parameter block
393 if (CurrentAnonymousMethod is AsyncInitializer)
394 return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait ||
395 local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
397 return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
400 public bool HasSet (Options options)
402 return (this.flags & options) == options;
405 public bool HasAny (Options options)
407 return (this.flags & options) != 0;
411 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
412 public FlagsHandle Set (Options options)
414 return new FlagsHandle (this, options);
417 public FlagsHandle With (Options options, bool enable)
419 return new FlagsHandle (this, options, enable ? options : 0);
422 #region IMemberContext Members
424 public string GetSignatureForError ()
426 return MemberContext.GetSignatureForError ();
429 public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity)
431 return MemberContext.LookupExtensionMethod (name, arity);
434 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
436 return MemberContext.LookupNamespaceOrType (name, arity, mode, loc);
439 public FullNamedExpression LookupNamespaceAlias (string name)
441 return MemberContext.LookupNamespaceAlias (name);
447 public class FlowAnalysisContext
449 readonly CompilerContext ctx;
451 public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength)
454 this.ParametersBlock = parametersBlock;
456 DefiniteAssignment = definiteAssignmentLength == 0 ?
457 DefiniteAssignmentBitSet.Empty :
458 new DefiniteAssignmentBitSet (definiteAssignmentLength);
461 public DefiniteAssignmentBitSet DefiniteAssignment { get; set; }
463 public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; }
465 public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; }
467 Dictionary<Statement, List<DefiniteAssignmentBitSet>> LabelStack { get; set; }
469 public ParametersBlock ParametersBlock { get; set; }
471 public Report Report {
477 public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; }
479 public TryFinally TryFinally { get; set; }
481 public bool UnreachableReported { get; set; }
483 public bool AddReachedLabel (Statement label)
485 List<DefiniteAssignmentBitSet> das;
486 if (LabelStack == null) {
487 LabelStack = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
490 LabelStack.TryGetValue (label, out das);
494 das = new List<DefiniteAssignmentBitSet> ();
495 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
496 LabelStack.Add (label, das);
500 foreach (var existing in das) {
501 if (DefiniteAssignmentBitSet.IsIncluded (existing, DefiniteAssignment))
505 if (DefiniteAssignment == DefiniteAssignmentBitSet.Empty)
506 das.Add (DefiniteAssignment);
508 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
513 public DefiniteAssignmentBitSet BranchDefiniteAssignment ()
515 return BranchDefiniteAssignment (DefiniteAssignment);
518 public DefiniteAssignmentBitSet BranchDefiniteAssignment (DefiniteAssignmentBitSet da)
520 if (da != DefiniteAssignmentBitSet.Empty) {
521 DefiniteAssignment = new DefiniteAssignmentBitSet (da);
527 public bool IsDefinitelyAssigned (VariableInfo variable)
529 return variable.IsAssigned (DefiniteAssignment);
532 public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
534 return variable.IsStructFieldAssigned (DefiniteAssignment, name);
537 public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
539 variable.SetAssigned (DefiniteAssignment, generatedAssignment);
542 public void SetStructFieldAssigned (VariableInfo variable, string name)
544 variable.SetStructFieldAssigned (DefiniteAssignment, name);
550 // This class is used during the Statement.Clone operation
551 // to remap objects that have been cloned.
553 // Since blocks are cloned by Block.Clone, we need a way for
554 // expressions that must reference the block to be cloned
555 // pointing to the new cloned block.
557 public class CloneContext
559 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
561 public void AddBlockMap (Block from, Block to)
563 block_map.Add (from, to);
566 public Block LookupBlock (Block from)
569 if (!block_map.TryGetValue (from, out result)) {
570 result = (Block) from.Clone (this);
577 /// Remaps block to cloned copy if one exists.
579 public Block RemapBlockCopy (Block from)
582 if (!block_map.TryGetValue (from, out mapped_to))
590 // Main compiler context
592 public class CompilerContext
594 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
596 readonly Report report;
597 readonly BuiltinTypes builtin_types;
598 readonly CompilerSettings settings;
600 Dictionary<string, SourceFile> all_source_files;
602 public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
604 this.settings = settings;
605 this.report = new Report (this, reportPrinter);
606 this.builtin_types = new BuiltinTypes ();
607 this.TimeReporter = DisabledTimeReporter;
612 public BuiltinTypes BuiltinTypes {
614 return builtin_types;
618 // Used for special handling of runtime dynamic context mostly
619 // by error reporting but also by member accessibility checks
620 public bool IsRuntimeBinder {
624 public Report Report {
630 public CompilerSettings Settings {
636 public List<SourceFile> SourceFiles {
638 return settings.SourceFiles;
642 internal TimeReporter TimeReporter {
649 // This is used when we encounter a #line preprocessing directive during parsing
650 // to register additional source file names
652 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
654 if (all_source_files == null) {
655 all_source_files = new Dictionary<string, SourceFile> ();
656 foreach (var source in SourceFiles)
657 all_source_files[source.FullPathName] = source;
661 if (!Path.IsPathRooted (name)) {
662 var loc = comp_unit.SourceFile;
663 string root = Path.GetDirectoryName (loc.FullPathName);
664 path = Path.GetFullPath (Path.Combine (root, name));
665 var dir = Path.GetDirectoryName (loc.Name);
666 if (!string.IsNullOrEmpty (dir))
667 name = Path.Combine (dir, name);
672 if (all_source_files.TryGetValue (path, out retval))
675 retval = new SourceFile (name, path, all_source_files.Count + 1);
676 Location.AddFile (retval);
677 all_source_files.Add (path, retval);
683 // Generic code emitter context
685 public class BuilderContext
691 /// This flag tracks the `checked' state of the compilation,
692 /// it controls whether we should generate code that does overflow
693 /// checking, or if we generate code that ignores overflows.
695 /// The default setting comes from the command line option to generate
696 /// checked or unchecked code plus any source code changes using the
697 /// checked/unchecked statements or expressions. Contrast this with
698 /// the ConstantCheckState flag.
700 CheckedScope = 1 << 0,
702 AccurateDebugInfo = 1 << 1,
704 OmitDebugInfo = 1 << 2,
706 ConstructorScope = 1 << 3,
711 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
712 // it's public so that we can use a struct at the callsite
713 public struct FlagsHandle : IDisposable
715 readonly BuilderContext ec;
716 readonly Options invmask, oldval;
718 public FlagsHandle (BuilderContext ec, Options flagsToSet)
719 : this (ec, flagsToSet, flagsToSet)
723 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
727 oldval = ec.flags & mask;
728 ec.flags = (ec.flags & invmask) | (val & mask);
731 public void Dispose ()
733 ec.flags = (ec.flags & invmask) | oldval;
737 protected Options flags;
739 public bool HasSet (Options options)
741 return (this.flags & options) == options;
744 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
745 public FlagsHandle With (Options options, bool enable)
747 return new FlagsHandle (this, options, enable ? options : 0);
752 // Parser session objects. We could recreate all these objects for each parser
753 // instance but the best parser performance the session object can be reused
755 public class ParserSession
759 public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
760 public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
761 public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
762 public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
763 public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
765 public LocationsBag LocationsBag { get; set; }
766 public bool UseJayGlobalArrays { get; set; }
767 public LocatedToken[] LocatedTokens { get; set; }
769 public MD5 GetChecksumAlgorithm ()
771 return md5 ?? (md5 = MD5.Create ());