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,
30 // Implemented by elements which can act as independent contexts
31 // during resolve phase. Used mostly for lookups.
33 public interface IMemberContext : IModuleContext
36 // A scope type context, it can be inflated for generic types
38 TypeSpec CurrentType { get; }
41 // A scope type parameters either VAR or MVAR
43 TypeParameters CurrentTypeParameters { get; }
46 // A member definition of the context. For partial types definition use
47 // CurrentTypeDefinition.PartialContainer otherwise the context is local
49 // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value
51 MemberCore CurrentMemberDefinition { get; }
53 bool IsObsolete { get; }
54 bool IsUnsafe { get; }
55 bool IsStatic { get; }
57 string GetSignatureForError ();
59 ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity);
60 FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc);
61 FullNamedExpression LookupNamespaceAlias (string name);
64 public interface IModuleContext
66 ModuleContainer Module { get; }
70 // Block or statement resolving context
72 public class BlockContext : ResolveContext
74 readonly TypeSpec return_type;
77 // Tracks the last offset used by VariableInfo
79 public int AssignmentInfoOffset;
81 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
84 if (returnType == null)
85 throw new ArgumentNullException ("returnType");
87 this.return_type = returnType;
89 // TODO: check for null value
93 public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
94 : this (rc.MemberContext, block, returnType)
97 flags |= ResolveContext.Options.UnsafeScope;
99 if (rc.HasSet (ResolveContext.Options.CheckedScope))
100 flags |= ResolveContext.Options.CheckedScope;
102 if (!rc.ConstantCheckState)
103 flags &= ~Options.ConstantCheckState;
105 if (rc.IsInProbingMode)
106 flags |= ResolveContext.Options.ProbingMode;
108 if (rc.HasSet (ResolveContext.Options.FieldInitializerScope))
109 flags |= ResolveContext.Options.FieldInitializerScope;
111 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
112 flags |= ResolveContext.Options.ExpressionTreeConversion;
114 if (rc.HasSet (ResolveContext.Options.BaseInitializer))
115 flags |= ResolveContext.Options.BaseInitializer;
118 public ExceptionStatement CurrentTryBlock { get; set; }
120 public LoopStatement EnclosingLoop { get; set; }
122 public LoopStatement EnclosingLoopOrSwitch { get; set; }
124 public Switch Switch { get; set; }
126 public TypeSpec ReturnType {
127 get { return return_type; }
132 // Expression resolving context
134 public class ResolveContext : IMemberContext
140 /// This flag tracks the `checked' state of the compilation,
141 /// it controls whether we should generate code that does overflow
142 /// checking, or if we generate code that ignores overflows.
144 /// The default setting comes from the command line option to generate
145 /// checked or unchecked code plus any source code changes using the
146 /// checked/unchecked statements or expressions. Contrast this with
147 /// the ConstantCheckState flag.
149 CheckedScope = 1 << 0,
152 /// The constant check state is always set to `true' and cant be changed
153 /// from the command line. The source code can change this setting with
154 /// the `checked' and `unchecked' statements and expressions.
156 ConstantCheckState = 1 << 1,
158 AllCheckStateFlags = CheckedScope | ConstantCheckState,
161 // unsafe { ... } scope
163 UnsafeScope = 1 << 2,
165 FinallyScope = 1 << 4,
166 FieldInitializerScope = 1 << 5,
167 CompoundAssignmentScope = 1 << 6,
168 FixedInitializerScope = 1 << 7,
169 BaseInitializer = 1 << 8,
172 // Inside an enum definition, we do not resolve enumeration values
173 // to their enumerations, but rather to the underlying type/value
174 // This is so EnumVal + EnumValB can be evaluated.
176 // There is no "E operator + (E x, E y)", so during an enum evaluation
177 // we relax the rules
181 ConstantScope = 1 << 10,
183 ConstructorScope = 1 << 11,
185 UsingInitializerScope = 1 << 12,
191 TryWithCatchScope = 1 << 15,
193 ConditionalAccessReceiver = 1 << 16,
196 /// Indicates the current context is in probing mode, no errors are reported.
198 ProbingMode = 1 << 22,
201 // Return and ContextualReturn statements will set the ReturnType
202 // value based on the expression types of each return statement
203 // instead of the method return type which is initially null.
205 InferReturnType = 1 << 23,
207 OmitDebuggingInfo = 1 << 24,
209 ExpressionTreeConversion = 1 << 25,
211 InvokeSpecialName = 1 << 26
214 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
215 // it's public so that we can use a struct at the callsite
216 public struct FlagsHandle : IDisposable
218 readonly ResolveContext ec;
219 readonly Options invmask, oldval;
221 public FlagsHandle (ResolveContext ec, Options flagsToSet)
222 : this (ec, flagsToSet, flagsToSet)
226 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
230 oldval = ec.flags & mask;
231 ec.flags = (ec.flags & invmask) | (val & mask);
233 // if ((mask & Options.ProbingMode) != 0)
234 // ec.Report.DisableReporting ();
237 public void Dispose ()
239 // if ((invmask & Options.ProbingMode) == 0)
240 // ec.Report.EnableReporting ();
242 ec.flags = (ec.flags & invmask) | oldval;
246 protected Options flags;
249 // Whether we are inside an anonymous method.
251 public AnonymousExpression CurrentAnonymousMethod;
254 // Holds a varible used during collection or object initialization.
256 public Expression CurrentInitializerVariable;
258 public Block CurrentBlock;
260 public readonly IMemberContext MemberContext;
262 public ResolveContext (IMemberContext mc)
265 throw new ArgumentNullException ();
270 // The default setting comes from the command line option
272 if (mc.Module.Compiler.Settings.Checked)
273 flags |= Options.CheckedScope;
276 // The constant check state is always set to true
278 flags |= Options.ConstantCheckState;
281 public ResolveContext (IMemberContext mc, Options options)
289 public BuiltinTypes BuiltinTypes {
291 return MemberContext.Module.Compiler.BuiltinTypes;
295 public virtual ExplicitBlock ConstructorBlock {
297 return CurrentBlock.Explicit;
302 // The current iterator
304 public Iterator CurrentIterator {
305 get { return CurrentAnonymousMethod as Iterator; }
308 public TypeSpec CurrentType {
309 get { return MemberContext.CurrentType; }
312 public TypeParameters CurrentTypeParameters {
313 get { return MemberContext.CurrentTypeParameters; }
316 public MemberCore CurrentMemberDefinition {
317 get { return MemberContext.CurrentMemberDefinition; }
320 public bool ConstantCheckState {
321 get { return (flags & Options.ConstantCheckState) != 0; }
324 public bool IsInProbingMode {
326 return (flags & Options.ProbingMode) != 0;
330 public bool IsObsolete {
332 // Disables obsolete checks when probing is on
333 return MemberContext.IsObsolete;
337 public bool IsStatic {
339 return MemberContext.IsStatic;
343 public bool IsUnsafe {
345 return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
349 public bool IsRuntimeBinder {
351 return Module.Compiler.IsRuntimeBinder;
355 public bool IsVariableCapturingRequired {
357 return !IsInProbingMode;
361 public ModuleContainer Module {
363 return MemberContext.Module;
367 public Report Report {
369 return Module.Compiler.Report;
375 public bool MustCaptureVariable (INamedBlockVariable local)
377 if (CurrentAnonymousMethod == null)
381 // Capture only if this or any of child blocks contain yield
382 // or it's a parameter
384 if (CurrentAnonymousMethod.IsIterator)
385 return local.IsParameter || local.Block.Explicit.HasYield;
388 // Capture only if this or any of child blocks contain await
389 // or it's a parameter or we need to access variable from
390 // different parameter block
392 if (CurrentAnonymousMethod is AsyncInitializer)
393 return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait ||
394 local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
396 return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
399 public bool HasSet (Options options)
401 return (this.flags & options) == options;
404 public bool HasAny (Options options)
406 return (this.flags & options) != 0;
410 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
411 public FlagsHandle Set (Options options)
413 return new FlagsHandle (this, options);
416 public FlagsHandle With (Options options, bool enable)
418 return new FlagsHandle (this, options, enable ? options : 0);
421 #region IMemberContext Members
423 public string GetSignatureForError ()
425 return MemberContext.GetSignatureForError ();
428 public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity)
430 return MemberContext.LookupExtensionMethod (extensionType, name, arity);
433 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
435 return MemberContext.LookupNamespaceOrType (name, arity, mode, loc);
438 public FullNamedExpression LookupNamespaceAlias (string name)
440 return MemberContext.LookupNamespaceAlias (name);
446 public class FlowAnalysisContext
448 readonly CompilerContext ctx;
449 DefiniteAssignmentBitSet conditional_access;
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.AreEqual (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 void BranchConditionalAccessDefiniteAssignment ()
529 if (conditional_access == null)
530 conditional_access = BranchDefiniteAssignment ();
533 public void ConditionalAccessEnd ()
535 Debug.Assert (conditional_access != null);
536 DefiniteAssignment = conditional_access;
537 conditional_access = null;
540 public bool IsDefinitelyAssigned (VariableInfo variable)
542 return variable.IsAssigned (DefiniteAssignment);
545 public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
547 return variable.IsStructFieldAssigned (DefiniteAssignment, name);
550 public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
552 variable.SetAssigned (DefiniteAssignment, generatedAssignment);
555 public void SetStructFieldAssigned (VariableInfo variable, string name)
557 variable.SetStructFieldAssigned (DefiniteAssignment, name);
563 // This class is used during the Statement.Clone operation
564 // to remap objects that have been cloned.
566 // Since blocks are cloned by Block.Clone, we need a way for
567 // expressions that must reference the block to be cloned
568 // pointing to the new cloned block.
570 public class CloneContext
572 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
574 public void AddBlockMap (Block from, Block to)
576 block_map.Add (from, to);
579 public Block LookupBlock (Block from)
582 if (!block_map.TryGetValue (from, out result)) {
583 result = (Block) from.Clone (this);
590 /// Remaps block to cloned copy if one exists.
592 public Block RemapBlockCopy (Block from)
595 if (!block_map.TryGetValue (from, out mapped_to))
603 // Main compiler context
605 public class CompilerContext
607 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
609 readonly Report report;
610 readonly BuiltinTypes builtin_types;
611 readonly CompilerSettings settings;
613 Dictionary<string, SourceFile> all_source_files;
615 public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
617 this.settings = settings;
618 this.report = new Report (this, reportPrinter);
619 this.builtin_types = new BuiltinTypes ();
620 this.TimeReporter = DisabledTimeReporter;
625 public BuiltinTypes BuiltinTypes {
627 return builtin_types;
631 // Used for special handling of runtime dynamic context mostly
632 // by error reporting but also by member accessibility checks
633 public bool IsRuntimeBinder {
637 public Report Report {
643 public CompilerSettings Settings {
649 public List<SourceFile> SourceFiles {
651 return settings.SourceFiles;
655 internal TimeReporter TimeReporter {
662 // This is used when we encounter a #line preprocessing directive during parsing
663 // to register additional source file names
665 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
667 if (all_source_files == null) {
668 all_source_files = new Dictionary<string, SourceFile> ();
669 foreach (var source in SourceFiles)
670 all_source_files[source.FullPathName] = source;
674 if (!Path.IsPathRooted (name)) {
675 var loc = comp_unit.SourceFile;
676 string root = Path.GetDirectoryName (loc.FullPathName);
677 path = Path.GetFullPath (Path.Combine (root, name));
678 var dir = Path.GetDirectoryName (loc.Name);
679 if (!string.IsNullOrEmpty (dir))
680 name = Path.Combine (dir, name);
685 if (all_source_files.TryGetValue (path, out retval))
688 retval = new SourceFile (name, path, all_source_files.Count + 1);
689 Location.AddFile (retval);
690 all_source_files.Add (path, retval);
696 // Generic code emitter context
698 public class BuilderContext
704 /// This flag tracks the `checked' state of the compilation,
705 /// it controls whether we should generate code that does overflow
706 /// checking, or if we generate code that ignores overflows.
708 /// The default setting comes from the command line option to generate
709 /// checked or unchecked code plus any source code changes using the
710 /// checked/unchecked statements or expressions. Contrast this with
711 /// the ConstantCheckState flag.
713 CheckedScope = 1 << 0,
715 AccurateDebugInfo = 1 << 1,
717 OmitDebugInfo = 1 << 2,
719 ConstructorScope = 1 << 3,
724 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
725 // it's public so that we can use a struct at the callsite
726 public struct FlagsHandle : IDisposable
728 readonly BuilderContext ec;
729 readonly Options invmask, oldval;
731 public FlagsHandle (BuilderContext ec, Options flagsToSet)
732 : this (ec, flagsToSet, flagsToSet)
736 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
740 oldval = ec.flags & mask;
741 ec.flags = (ec.flags & invmask) | (val & mask);
744 public void Dispose ()
746 ec.flags = (ec.flags & invmask) | oldval;
750 protected Options flags;
752 public bool HasSet (Options options)
754 return (this.flags & options) == options;
757 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
758 public FlagsHandle With (Options options, bool enable)
760 return new FlagsHandle (this, options, enable ? options : 0);
765 // Parser session objects. We could recreate all these objects for each parser
766 // instance but the best parser performance the session object can be reused
768 public class ParserSession
772 public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
773 public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
774 public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
775 public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
776 public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
778 public LocationsBag LocationsBag { get; set; }
779 public bool UseJayGlobalArrays { get; set; }
780 public LocatedToken[] LocatedTokens { get; set; }
782 public MD5 GetChecksumAlgorithm ()
784 return md5 ?? (md5 = MD5.Create ());