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,
195 /// Indicates the current context is in probing mode, no errors are reported.
197 ProbingMode = 1 << 22,
200 // Return and ContextualReturn statements will set the ReturnType
201 // value based on the expression types of each return statement
202 // instead of the method return type which is initially null.
204 InferReturnType = 1 << 23,
206 OmitDebuggingInfo = 1 << 24,
208 ExpressionTreeConversion = 1 << 25,
210 InvokeSpecialName = 1 << 26
213 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
214 // it's public so that we can use a struct at the callsite
215 public struct FlagsHandle : IDisposable
217 readonly ResolveContext ec;
218 readonly Options invmask, oldval;
220 public FlagsHandle (ResolveContext ec, Options flagsToSet)
221 : this (ec, flagsToSet, flagsToSet)
225 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
229 oldval = ec.flags & mask;
230 ec.flags = (ec.flags & invmask) | (val & mask);
232 // if ((mask & Options.ProbingMode) != 0)
233 // ec.Report.DisableReporting ();
236 public void Dispose ()
238 // if ((invmask & Options.ProbingMode) == 0)
239 // ec.Report.EnableReporting ();
241 ec.flags = (ec.flags & invmask) | oldval;
245 protected Options flags;
248 // Whether we are inside an anonymous method.
250 public AnonymousExpression CurrentAnonymousMethod;
253 // Holds a varible used during collection or object initialization.
255 public Expression CurrentInitializerVariable;
257 public Block CurrentBlock;
259 public readonly IMemberContext MemberContext;
261 public ResolveContext (IMemberContext mc)
264 throw new ArgumentNullException ();
269 // The default setting comes from the command line option
271 if (mc.Module.Compiler.Settings.Checked)
272 flags |= Options.CheckedScope;
275 // The constant check state is always set to true
277 flags |= Options.ConstantCheckState;
280 public ResolveContext (IMemberContext mc, Options options)
288 public BuiltinTypes BuiltinTypes {
290 return MemberContext.Module.Compiler.BuiltinTypes;
294 public virtual ExplicitBlock ConstructorBlock {
296 return CurrentBlock.Explicit;
301 // The current iterator
303 public Iterator CurrentIterator {
304 get { return CurrentAnonymousMethod as Iterator; }
307 public TypeSpec CurrentType {
308 get { return MemberContext.CurrentType; }
311 public TypeParameters CurrentTypeParameters {
312 get { return MemberContext.CurrentTypeParameters; }
315 public MemberCore CurrentMemberDefinition {
316 get { return MemberContext.CurrentMemberDefinition; }
319 public bool ConstantCheckState {
320 get { return (flags & Options.ConstantCheckState) != 0; }
323 public bool IsInProbingMode {
325 return (flags & Options.ProbingMode) != 0;
329 public bool IsObsolete {
331 // Disables obsolete checks when probing is on
332 return MemberContext.IsObsolete;
336 public bool IsStatic {
338 return MemberContext.IsStatic;
342 public bool IsUnsafe {
344 return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
348 public bool IsRuntimeBinder {
350 return Module.Compiler.IsRuntimeBinder;
354 public bool IsVariableCapturingRequired {
356 return !IsInProbingMode;
360 public ModuleContainer Module {
362 return MemberContext.Module;
366 public Report Report {
368 return Module.Compiler.Report;
374 public bool MustCaptureVariable (INamedBlockVariable local)
376 if (CurrentAnonymousMethod == null)
380 // Capture only if this or any of child blocks contain yield
381 // or it's a parameter
383 if (CurrentAnonymousMethod.IsIterator)
384 return local.IsParameter || local.Block.Explicit.HasYield;
387 // Capture only if this or any of child blocks contain await
388 // or it's a parameter or we need to access variable from
389 // different parameter block
391 if (CurrentAnonymousMethod is AsyncInitializer)
392 return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait ||
393 local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
395 return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
398 public bool HasSet (Options options)
400 return (this.flags & options) == options;
403 public bool HasAny (Options options)
405 return (this.flags & options) != 0;
409 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
410 public FlagsHandle Set (Options options)
412 return new FlagsHandle (this, options);
415 public FlagsHandle With (Options options, bool enable)
417 return new FlagsHandle (this, options, enable ? options : 0);
420 #region IMemberContext Members
422 public string GetSignatureForError ()
424 return MemberContext.GetSignatureForError ();
427 public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity)
429 return MemberContext.LookupExtensionMethod (name, arity);
432 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
434 return MemberContext.LookupNamespaceOrType (name, arity, mode, loc);
437 public FullNamedExpression LookupNamespaceAlias (string name)
439 return MemberContext.LookupNamespaceAlias (name);
445 public class FlowAnalysisContext
447 readonly CompilerContext ctx;
449 public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength)
452 this.ParametersBlock = parametersBlock;
454 DefiniteAssignment = definiteAssignmentLength == 0 ?
455 DefiniteAssignmentBitSet.Empty :
456 new DefiniteAssignmentBitSet (definiteAssignmentLength);
459 public DefiniteAssignmentBitSet DefiniteAssignment { get; set; }
461 public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; }
463 public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; }
465 Dictionary<Statement, List<DefiniteAssignmentBitSet>> LabelStack { get; set; }
467 public ParametersBlock ParametersBlock { get; set; }
469 public Report Report {
475 public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; }
477 public TryFinally TryFinally { get; set; }
479 public bool UnreachableReported { get; set; }
481 public bool AddReachedLabel (Statement label)
483 List<DefiniteAssignmentBitSet> das;
484 if (LabelStack == null) {
485 LabelStack = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
488 LabelStack.TryGetValue (label, out das);
492 das = new List<DefiniteAssignmentBitSet> ();
493 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
494 LabelStack.Add (label, das);
498 foreach (var existing in das) {
499 if (DefiniteAssignmentBitSet.IsIncluded (existing, DefiniteAssignment))
503 if (DefiniteAssignment == DefiniteAssignmentBitSet.Empty)
504 das.Add (DefiniteAssignment);
506 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
511 public DefiniteAssignmentBitSet BranchDefiniteAssignment ()
513 return BranchDefiniteAssignment (DefiniteAssignment);
516 public DefiniteAssignmentBitSet BranchDefiniteAssignment (DefiniteAssignmentBitSet da)
518 if (da != DefiniteAssignmentBitSet.Empty) {
519 DefiniteAssignment = new DefiniteAssignmentBitSet (da);
525 public bool IsDefinitelyAssigned (VariableInfo variable)
527 return variable.IsAssigned (DefiniteAssignment);
530 public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
532 return variable.IsStructFieldAssigned (DefiniteAssignment, name);
535 public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
537 variable.SetAssigned (DefiniteAssignment, generatedAssignment);
540 public void SetStructFieldAssigned (VariableInfo variable, string name)
542 variable.SetStructFieldAssigned (DefiniteAssignment, name);
548 // This class is used during the Statement.Clone operation
549 // to remap objects that have been cloned.
551 // Since blocks are cloned by Block.Clone, we need a way for
552 // expressions that must reference the block to be cloned
553 // pointing to the new cloned block.
555 public class CloneContext
557 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
559 public void AddBlockMap (Block from, Block to)
561 block_map.Add (from, to);
564 public Block LookupBlock (Block from)
567 if (!block_map.TryGetValue (from, out result)) {
568 result = (Block) from.Clone (this);
575 /// Remaps block to cloned copy if one exists.
577 public Block RemapBlockCopy (Block from)
580 if (!block_map.TryGetValue (from, out mapped_to))
588 // Main compiler context
590 public class CompilerContext
592 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
594 readonly Report report;
595 readonly BuiltinTypes builtin_types;
596 readonly CompilerSettings settings;
598 Dictionary<string, SourceFile> all_source_files;
600 public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
602 this.settings = settings;
603 this.report = new Report (this, reportPrinter);
604 this.builtin_types = new BuiltinTypes ();
605 this.TimeReporter = DisabledTimeReporter;
610 public BuiltinTypes BuiltinTypes {
612 return builtin_types;
616 // Used for special handling of runtime dynamic context mostly
617 // by error reporting but also by member accessibility checks
618 public bool IsRuntimeBinder {
622 public Report Report {
628 public CompilerSettings Settings {
634 public List<SourceFile> SourceFiles {
636 return settings.SourceFiles;
640 internal TimeReporter TimeReporter {
647 // This is used when we encounter a #line preprocessing directive during parsing
648 // to register additional source file names
650 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
652 if (all_source_files == null) {
653 all_source_files = new Dictionary<string, SourceFile> ();
654 foreach (var source in SourceFiles)
655 all_source_files[source.FullPathName] = source;
659 if (!Path.IsPathRooted (name)) {
660 var loc = comp_unit.SourceFile;
661 string root = Path.GetDirectoryName (loc.FullPathName);
662 path = Path.GetFullPath (Path.Combine (root, name));
663 var dir = Path.GetDirectoryName (loc.Name);
664 if (!string.IsNullOrEmpty (dir))
665 name = Path.Combine (dir, name);
670 if (all_source_files.TryGetValue (path, out retval))
673 retval = new SourceFile (name, path, all_source_files.Count + 1);
674 Location.AddFile (retval);
675 all_source_files.Add (path, retval);
681 // Generic code emitter context
683 public class BuilderContext
689 /// This flag tracks the `checked' state of the compilation,
690 /// it controls whether we should generate code that does overflow
691 /// checking, or if we generate code that ignores overflows.
693 /// The default setting comes from the command line option to generate
694 /// checked or unchecked code plus any source code changes using the
695 /// checked/unchecked statements or expressions. Contrast this with
696 /// the ConstantCheckState flag.
698 CheckedScope = 1 << 0,
700 AccurateDebugInfo = 1 << 1,
702 OmitDebugInfo = 1 << 2,
704 ConstructorScope = 1 << 3,
709 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
710 // it's public so that we can use a struct at the callsite
711 public struct FlagsHandle : IDisposable
713 readonly BuilderContext ec;
714 readonly Options invmask, oldval;
716 public FlagsHandle (BuilderContext ec, Options flagsToSet)
717 : this (ec, flagsToSet, flagsToSet)
721 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
725 oldval = ec.flags & mask;
726 ec.flags = (ec.flags & invmask) | (val & mask);
729 public void Dispose ()
731 ec.flags = (ec.flags & invmask) | oldval;
735 protected Options flags;
737 public bool HasSet (Options options)
739 return (this.flags & options) == options;
742 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
743 public FlagsHandle With (Options options, bool enable)
745 return new FlagsHandle (this, options, enable ? options : 0);
750 // Parser session objects. We could recreate all these objects for each parser
751 // instance but the best parser performance the session object can be reused
753 public class ParserSession
757 public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
758 public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
759 public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
760 public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
761 public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
763 public LocationsBag LocationsBag { get; set; }
764 public bool UseJayGlobalArrays { get; set; }
765 public LocatedToken[] LocatedTokens { get; set; }
767 public MD5 GetChecksumAlgorithm ()
769 return md5 ?? (md5 = MD5.Create ());