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 ConditionalAccessReceiver = 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;
448 DefiniteAssignmentBitSet conditional_access;
450 public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength)
453 this.ParametersBlock = parametersBlock;
455 DefiniteAssignment = definiteAssignmentLength == 0 ?
456 DefiniteAssignmentBitSet.Empty :
457 new DefiniteAssignmentBitSet (definiteAssignmentLength);
460 public DefiniteAssignmentBitSet DefiniteAssignment { get; set; }
462 public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; }
464 public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; }
466 Dictionary<Statement, List<DefiniteAssignmentBitSet>> LabelStack { get; set; }
468 public ParametersBlock ParametersBlock { get; set; }
470 public Report Report {
476 public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; }
478 public TryFinally TryFinally { get; set; }
480 public bool UnreachableReported { get; set; }
482 public bool AddReachedLabel (Statement label)
484 List<DefiniteAssignmentBitSet> das;
485 if (LabelStack == null) {
486 LabelStack = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
489 LabelStack.TryGetValue (label, out das);
493 das = new List<DefiniteAssignmentBitSet> ();
494 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
495 LabelStack.Add (label, das);
499 foreach (var existing in das) {
500 if (DefiniteAssignmentBitSet.IsIncluded (existing, DefiniteAssignment))
504 if (DefiniteAssignment == DefiniteAssignmentBitSet.Empty)
505 das.Add (DefiniteAssignment);
507 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
512 public DefiniteAssignmentBitSet BranchDefiniteAssignment ()
514 return BranchDefiniteAssignment (DefiniteAssignment);
517 public DefiniteAssignmentBitSet BranchDefiniteAssignment (DefiniteAssignmentBitSet da)
519 if (da != DefiniteAssignmentBitSet.Empty) {
520 DefiniteAssignment = new DefiniteAssignmentBitSet (da);
526 public void BranchConditionalAccessDefiniteAssignment ()
528 if (conditional_access == null)
529 conditional_access = BranchDefiniteAssignment ();
532 public void ConditionalAccessEnd ()
534 Debug.Assert (conditional_access != null);
535 DefiniteAssignment = conditional_access;
536 conditional_access = null;
539 public bool IsDefinitelyAssigned (VariableInfo variable)
541 return variable.IsAssigned (DefiniteAssignment);
544 public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
546 return variable.IsStructFieldAssigned (DefiniteAssignment, name);
549 public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
551 variable.SetAssigned (DefiniteAssignment, generatedAssignment);
554 public void SetStructFieldAssigned (VariableInfo variable, string name)
556 variable.SetStructFieldAssigned (DefiniteAssignment, name);
562 // This class is used during the Statement.Clone operation
563 // to remap objects that have been cloned.
565 // Since blocks are cloned by Block.Clone, we need a way for
566 // expressions that must reference the block to be cloned
567 // pointing to the new cloned block.
569 public class CloneContext
571 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
573 public void AddBlockMap (Block from, Block to)
575 block_map.Add (from, to);
578 public Block LookupBlock (Block from)
581 if (!block_map.TryGetValue (from, out result)) {
582 result = (Block) from.Clone (this);
589 /// Remaps block to cloned copy if one exists.
591 public Block RemapBlockCopy (Block from)
594 if (!block_map.TryGetValue (from, out mapped_to))
602 // Main compiler context
604 public class CompilerContext
606 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
608 readonly Report report;
609 readonly BuiltinTypes builtin_types;
610 readonly CompilerSettings settings;
612 Dictionary<string, SourceFile> all_source_files;
614 public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
616 this.settings = settings;
617 this.report = new Report (this, reportPrinter);
618 this.builtin_types = new BuiltinTypes ();
619 this.TimeReporter = DisabledTimeReporter;
624 public BuiltinTypes BuiltinTypes {
626 return builtin_types;
630 // Used for special handling of runtime dynamic context mostly
631 // by error reporting but also by member accessibility checks
632 public bool IsRuntimeBinder {
636 public Report Report {
642 public CompilerSettings Settings {
648 public List<SourceFile> SourceFiles {
650 return settings.SourceFiles;
654 internal TimeReporter TimeReporter {
661 // This is used when we encounter a #line preprocessing directive during parsing
662 // to register additional source file names
664 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
666 if (all_source_files == null) {
667 all_source_files = new Dictionary<string, SourceFile> ();
668 foreach (var source in SourceFiles)
669 all_source_files[source.FullPathName] = source;
673 if (!Path.IsPathRooted (name)) {
674 var loc = comp_unit.SourceFile;
675 string root = Path.GetDirectoryName (loc.FullPathName);
676 path = Path.GetFullPath (Path.Combine (root, name));
677 var dir = Path.GetDirectoryName (loc.Name);
678 if (!string.IsNullOrEmpty (dir))
679 name = Path.Combine (dir, name);
684 if (all_source_files.TryGetValue (path, out retval))
687 retval = new SourceFile (name, path, all_source_files.Count + 1);
688 Location.AddFile (retval);
689 all_source_files.Add (path, retval);
695 // Generic code emitter context
697 public class BuilderContext
703 /// This flag tracks the `checked' state of the compilation,
704 /// it controls whether we should generate code that does overflow
705 /// checking, or if we generate code that ignores overflows.
707 /// The default setting comes from the command line option to generate
708 /// checked or unchecked code plus any source code changes using the
709 /// checked/unchecked statements or expressions. Contrast this with
710 /// the ConstantCheckState flag.
712 CheckedScope = 1 << 0,
714 AccurateDebugInfo = 1 << 1,
716 OmitDebugInfo = 1 << 2,
718 ConstructorScope = 1 << 3,
723 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
724 // it's public so that we can use a struct at the callsite
725 public struct FlagsHandle : IDisposable
727 readonly BuilderContext ec;
728 readonly Options invmask, oldval;
730 public FlagsHandle (BuilderContext ec, Options flagsToSet)
731 : this (ec, flagsToSet, flagsToSet)
735 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
739 oldval = ec.flags & mask;
740 ec.flags = (ec.flags & invmask) | (val & mask);
743 public void Dispose ()
745 ec.flags = (ec.flags & invmask) | oldval;
749 protected Options flags;
751 public bool HasSet (Options options)
753 return (this.flags & options) == options;
756 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
757 public FlagsHandle With (Options options, bool enable)
759 return new FlagsHandle (this, options, enable ? options : 0);
764 // Parser session objects. We could recreate all these objects for each parser
765 // instance but the best parser performance the session object can be reused
767 public class ParserSession
771 public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
772 public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
773 public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
774 public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
775 public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
777 public LocationsBag LocationsBag { get; set; }
778 public bool UseJayGlobalArrays { get; set; }
779 public LocatedToken[] LocatedTokens { get; set; }
781 public MD5 GetChecksumAlgorithm ()
783 return md5 ?? (md5 = MD5.Create ());