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 TryCatch CurrentTryCatch { get; set; }
121 public LoopStatement EnclosingLoop { get; set; }
123 public LoopStatement EnclosingLoopOrSwitch { get; set; }
125 public Switch Switch { get; set; }
127 public TypeSpec ReturnType {
128 get { return return_type; }
133 // Expression resolving context
135 public class ResolveContext : IMemberContext
141 /// This flag tracks the `checked' state of the compilation,
142 /// it controls whether we should generate code that does overflow
143 /// checking, or if we generate code that ignores overflows.
145 /// The default setting comes from the command line option to generate
146 /// checked or unchecked code plus any source code changes using the
147 /// checked/unchecked statements or expressions. Contrast this with
148 /// the ConstantCheckState flag.
150 CheckedScope = 1 << 0,
153 /// The constant check state is always set to `true' and cant be changed
154 /// from the command line. The source code can change this setting with
155 /// the `checked' and `unchecked' statements and expressions.
157 ConstantCheckState = 1 << 1,
159 AllCheckStateFlags = CheckedScope | ConstantCheckState,
162 // unsafe { ... } scope
164 UnsafeScope = 1 << 2,
166 FinallyScope = 1 << 4,
167 FieldInitializerScope = 1 << 5,
168 CompoundAssignmentScope = 1 << 6,
169 FixedInitializerScope = 1 << 7,
170 BaseInitializer = 1 << 8,
173 // Inside an enum definition, we do not resolve enumeration values
174 // to their enumerations, but rather to the underlying type/value
175 // This is so EnumVal + EnumValB can be evaluated.
177 // There is no "E operator + (E x, E y)", so during an enum evaluation
178 // we relax the rules
182 ConstantScope = 1 << 10,
184 ConstructorScope = 1 << 11,
186 UsingInitializerScope = 1 << 12,
192 TryWithCatchScope = 1 << 15,
194 DontSetConditionalAccessReceiver = 1 << 16,
196 NameOfScope = 1 << 17,
199 /// Indicates the current context is in probing mode, no errors are reported.
201 ProbingMode = 1 << 22,
204 // Return and ContextualReturn statements will set the ReturnType
205 // value based on the expression types of each return statement
206 // instead of the method return type which is initially null.
208 InferReturnType = 1 << 23,
210 OmitDebuggingInfo = 1 << 24,
212 ExpressionTreeConversion = 1 << 25,
214 InvokeSpecialName = 1 << 26
217 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
218 // it's public so that we can use a struct at the callsite
219 public struct FlagsHandle : IDisposable
221 readonly ResolveContext ec;
222 readonly Options invmask, oldval;
224 public FlagsHandle (ResolveContext ec, Options flagsToSet)
225 : this (ec, flagsToSet, flagsToSet)
229 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
233 oldval = ec.flags & mask;
234 ec.flags = (ec.flags & invmask) | (val & mask);
236 // if ((mask & Options.ProbingMode) != 0)
237 // ec.Report.DisableReporting ();
240 public void Dispose ()
242 // if ((invmask & Options.ProbingMode) == 0)
243 // ec.Report.EnableReporting ();
245 ec.flags = (ec.flags & invmask) | oldval;
249 protected Options flags;
252 // Whether we are inside an anonymous method.
254 public AnonymousExpression CurrentAnonymousMethod;
257 // Holds a varible used during collection or object initialization.
259 public Expression CurrentInitializerVariable;
261 public Block CurrentBlock;
263 public readonly IMemberContext MemberContext;
265 public ResolveContext (IMemberContext mc)
268 throw new ArgumentNullException ();
273 // The default setting comes from the command line option
275 if (mc.Module.Compiler.Settings.Checked)
276 flags |= Options.CheckedScope;
279 // The constant check state is always set to true
281 flags |= Options.ConstantCheckState;
284 public ResolveContext (IMemberContext mc, Options options)
292 public BuiltinTypes BuiltinTypes {
294 return MemberContext.Module.Compiler.BuiltinTypes;
298 public virtual ExplicitBlock ConstructorBlock {
300 return CurrentBlock.Explicit;
305 // The current iterator
307 public Iterator CurrentIterator {
308 get { return CurrentAnonymousMethod as Iterator; }
311 public TypeSpec CurrentType {
312 get { return MemberContext.CurrentType; }
315 public TypeParameters CurrentTypeParameters {
316 get { return MemberContext.CurrentTypeParameters; }
319 public MemberCore CurrentMemberDefinition {
320 get { return MemberContext.CurrentMemberDefinition; }
323 public bool ConstantCheckState {
324 get { return (flags & Options.ConstantCheckState) != 0; }
327 public bool IsInProbingMode {
329 return (flags & Options.ProbingMode) != 0;
333 public bool IsObsolete {
335 // Disables obsolete checks when probing is on
336 return MemberContext.IsObsolete;
340 public bool IsStatic {
342 return MemberContext.IsStatic;
346 public bool IsUnsafe {
348 return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
352 public bool IsRuntimeBinder {
354 return Module.Compiler.IsRuntimeBinder;
358 public bool IsVariableCapturingRequired {
360 return !IsInProbingMode;
364 public ModuleContainer Module {
366 return MemberContext.Module;
370 public Report Report {
372 return Module.Compiler.Report;
378 public bool MustCaptureVariable (INamedBlockVariable local)
380 if (CurrentAnonymousMethod == null)
384 // Capture only if this or any of child blocks contain yield
385 // or it's a parameter
387 if (CurrentAnonymousMethod.IsIterator)
388 return local.IsParameter || local.Block.Explicit.HasYield;
391 // Capture only if this or any of child blocks contain await
392 // or it's a parameter or we need to access variable from
393 // different parameter block
395 if (CurrentAnonymousMethod is AsyncInitializer)
396 return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait ||
397 local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
399 return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
402 public bool HasSet (Options options)
404 return (this.flags & options) == options;
407 public bool HasAny (Options options)
409 return (this.flags & options) != 0;
413 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
414 public FlagsHandle Set (Options options)
416 return new FlagsHandle (this, options);
419 public FlagsHandle With (Options options, bool enable)
421 return new FlagsHandle (this, options, enable ? options : 0);
424 #region IMemberContext Members
426 public string GetSignatureForError ()
428 return MemberContext.GetSignatureForError ();
431 public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity)
433 return MemberContext.LookupExtensionMethod (name, arity);
436 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
438 return MemberContext.LookupNamespaceOrType (name, arity, mode, loc);
441 public FullNamedExpression LookupNamespaceAlias (string name)
443 return MemberContext.LookupNamespaceAlias (name);
449 public class FlowAnalysisContext
451 readonly CompilerContext ctx;
453 public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength)
456 this.ParametersBlock = parametersBlock;
458 DefiniteAssignment = definiteAssignmentLength == 0 ?
459 DefiniteAssignmentBitSet.Empty :
460 new DefiniteAssignmentBitSet (definiteAssignmentLength);
463 public DefiniteAssignmentBitSet DefiniteAssignment { get; set; }
465 public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; }
467 public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; }
469 Dictionary<Statement, List<DefiniteAssignmentBitSet>> LabelStack { get; set; }
471 public ParametersBlock ParametersBlock { get; set; }
473 public Report Report {
479 public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; }
481 public TryFinally TryFinally { get; set; }
483 public bool UnreachableReported { get; set; }
485 public bool AddReachedLabel (Statement label)
487 List<DefiniteAssignmentBitSet> das;
488 if (LabelStack == null) {
489 LabelStack = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
492 LabelStack.TryGetValue (label, out das);
496 das = new List<DefiniteAssignmentBitSet> ();
497 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
498 LabelStack.Add (label, das);
502 foreach (var existing in das) {
503 if (DefiniteAssignmentBitSet.IsIncluded (existing, DefiniteAssignment))
507 if (DefiniteAssignment == DefiniteAssignmentBitSet.Empty)
508 das.Add (DefiniteAssignment);
510 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
515 public DefiniteAssignmentBitSet BranchDefiniteAssignment ()
517 return BranchDefiniteAssignment (DefiniteAssignment);
520 public DefiniteAssignmentBitSet BranchDefiniteAssignment (DefiniteAssignmentBitSet da)
522 if (da != DefiniteAssignmentBitSet.Empty) {
523 DefiniteAssignment = new DefiniteAssignmentBitSet (da);
529 public Dictionary<Statement, List<DefiniteAssignmentBitSet>> CopyLabelStack ()
531 if (LabelStack == null)
534 var dest = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
535 foreach (var entry in LabelStack) {
536 dest.Add (entry.Key, new List<DefiniteAssignmentBitSet> (entry.Value));
542 public bool IsDefinitelyAssigned (VariableInfo variable)
544 return variable.IsAssigned (DefiniteAssignment);
547 public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
549 return variable.IsStructFieldAssigned (DefiniteAssignment, name);
552 public void SetLabelStack (Dictionary<Statement, List<DefiniteAssignmentBitSet>> labelStack)
554 LabelStack = labelStack;
557 public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
559 variable.SetAssigned (DefiniteAssignment, generatedAssignment);
562 public void SetStructFieldAssigned (VariableInfo variable, string name)
564 variable.SetStructFieldAssigned (DefiniteAssignment, name);
570 // This class is used during the Statement.Clone operation
571 // to remap objects that have been cloned.
573 // Since blocks are cloned by Block.Clone, we need a way for
574 // expressions that must reference the block to be cloned
575 // pointing to the new cloned block.
577 public class CloneContext
579 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
581 public void AddBlockMap (Block from, Block to)
583 block_map.Add (from, to);
586 public Block LookupBlock (Block from)
589 if (!block_map.TryGetValue (from, out result)) {
590 result = (Block) from.Clone (this);
597 /// Remaps block to cloned copy if one exists.
599 public Block RemapBlockCopy (Block from)
602 if (!block_map.TryGetValue (from, out mapped_to))
610 // Main compiler context
612 public class CompilerContext
614 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
616 readonly Report report;
617 readonly BuiltinTypes builtin_types;
618 readonly CompilerSettings settings;
620 Dictionary<string, SourceFile> all_source_files;
622 public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
624 this.settings = settings;
625 this.report = new Report (this, reportPrinter);
626 this.builtin_types = new BuiltinTypes ();
627 this.TimeReporter = DisabledTimeReporter;
632 public BuiltinTypes BuiltinTypes {
634 return builtin_types;
638 // Used for special handling of runtime dynamic context mostly
639 // by error reporting but also by member accessibility checks
640 public bool IsRuntimeBinder {
644 public Report Report {
650 public CompilerSettings Settings {
656 public List<SourceFile> SourceFiles {
658 return settings.SourceFiles;
662 internal TimeReporter TimeReporter {
669 // This is used when we encounter a #line preprocessing directive during parsing
670 // to register additional source file names
672 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
674 if (all_source_files == null) {
675 all_source_files = new Dictionary<string, SourceFile> ();
676 foreach (var source in SourceFiles)
677 all_source_files[source.FullPathName] = source;
681 if (!Path.IsPathRooted (name)) {
682 var loc = comp_unit.SourceFile;
683 string root = Path.GetDirectoryName (loc.FullPathName);
684 path = Path.GetFullPath (Path.Combine (root, name));
685 var dir = Path.GetDirectoryName (loc.Name);
686 if (!string.IsNullOrEmpty (dir))
687 name = Path.Combine (dir, name);
692 if (all_source_files.TryGetValue (path, out retval))
695 retval = new SourceFile (name, path, all_source_files.Count + 1);
696 Location.AddFile (retval);
697 all_source_files.Add (path, retval);
703 // Generic code emitter context
705 public class BuilderContext
711 /// This flag tracks the `checked' state of the compilation,
712 /// it controls whether we should generate code that does overflow
713 /// checking, or if we generate code that ignores overflows.
715 /// The default setting comes from the command line option to generate
716 /// checked or unchecked code plus any source code changes using the
717 /// checked/unchecked statements or expressions. Contrast this with
718 /// the ConstantCheckState flag.
720 CheckedScope = 1 << 0,
722 AccurateDebugInfo = 1 << 1,
724 OmitDebugInfo = 1 << 2,
726 ConstructorScope = 1 << 3,
731 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
732 // it's public so that we can use a struct at the callsite
733 public struct FlagsHandle : IDisposable
735 readonly BuilderContext ec;
736 readonly Options invmask, oldval;
738 public FlagsHandle (BuilderContext ec, Options flagsToSet)
739 : this (ec, flagsToSet, flagsToSet)
743 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
747 oldval = ec.flags & mask;
748 ec.flags = (ec.flags & invmask) | (val & mask);
751 public void Dispose ()
753 ec.flags = (ec.flags & invmask) | oldval;
757 protected Options flags;
759 public bool HasSet (Options options)
761 return (this.flags & options) == options;
764 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
765 public FlagsHandle With (Options options, bool enable)
767 return new FlagsHandle (this, options, enable ? options : 0);
772 // Parser session objects. We could recreate all these objects for each parser
773 // instance but the best parser performance the session object can be reused
775 public class ParserSession
779 public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
780 public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
781 public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
782 public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
783 public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
785 public LocationsBag LocationsBag { get; set; }
786 public bool UseJayGlobalArrays { get; set; }
787 public LocatedToken[] LocatedTokens { get; set; }
789 public MD5 GetChecksumAlgorithm ()
791 return md5 ?? (md5 = MD5.Create ());