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 bool IsDefinitelyAssigned (VariableInfo variable)
531 return variable.IsAssigned (DefiniteAssignment);
534 public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
536 return variable.IsStructFieldAssigned (DefiniteAssignment, name);
539 public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
541 variable.SetAssigned (DefiniteAssignment, generatedAssignment);
544 public void SetStructFieldAssigned (VariableInfo variable, string name)
546 variable.SetStructFieldAssigned (DefiniteAssignment, name);
552 // This class is used during the Statement.Clone operation
553 // to remap objects that have been cloned.
555 // Since blocks are cloned by Block.Clone, we need a way for
556 // expressions that must reference the block to be cloned
557 // pointing to the new cloned block.
559 public class CloneContext
561 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
563 public void AddBlockMap (Block from, Block to)
565 block_map.Add (from, to);
568 public Block LookupBlock (Block from)
571 if (!block_map.TryGetValue (from, out result)) {
572 result = (Block) from.Clone (this);
579 /// Remaps block to cloned copy if one exists.
581 public Block RemapBlockCopy (Block from)
584 if (!block_map.TryGetValue (from, out mapped_to))
592 // Main compiler context
594 public class CompilerContext
596 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
598 readonly Report report;
599 readonly BuiltinTypes builtin_types;
600 readonly CompilerSettings settings;
602 Dictionary<string, SourceFile> all_source_files;
604 public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
606 this.settings = settings;
607 this.report = new Report (this, reportPrinter);
608 this.builtin_types = new BuiltinTypes ();
609 this.TimeReporter = DisabledTimeReporter;
614 public BuiltinTypes BuiltinTypes {
616 return builtin_types;
620 // Used for special handling of runtime dynamic context mostly
621 // by error reporting but also by member accessibility checks
622 public bool IsRuntimeBinder {
626 public Report Report {
632 public CompilerSettings Settings {
638 public List<SourceFile> SourceFiles {
640 return settings.SourceFiles;
644 internal TimeReporter TimeReporter {
651 // This is used when we encounter a #line preprocessing directive during parsing
652 // to register additional source file names
654 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
656 if (all_source_files == null) {
657 all_source_files = new Dictionary<string, SourceFile> ();
658 foreach (var source in SourceFiles)
659 all_source_files[source.FullPathName] = source;
663 if (!Path.IsPathRooted (name)) {
664 var loc = comp_unit.SourceFile;
665 string root = Path.GetDirectoryName (loc.FullPathName);
666 path = Path.GetFullPath (Path.Combine (root, name));
667 var dir = Path.GetDirectoryName (loc.Name);
668 if (!string.IsNullOrEmpty (dir))
669 name = Path.Combine (dir, name);
674 if (all_source_files.TryGetValue (path, out retval))
677 retval = new SourceFile (name, path, all_source_files.Count + 1);
678 Location.AddFile (retval);
679 all_source_files.Add (path, retval);
685 // Generic code emitter context
687 public class BuilderContext
693 /// This flag tracks the `checked' state of the compilation,
694 /// it controls whether we should generate code that does overflow
695 /// checking, or if we generate code that ignores overflows.
697 /// The default setting comes from the command line option to generate
698 /// checked or unchecked code plus any source code changes using the
699 /// checked/unchecked statements or expressions. Contrast this with
700 /// the ConstantCheckState flag.
702 CheckedScope = 1 << 0,
704 AccurateDebugInfo = 1 << 1,
706 OmitDebugInfo = 1 << 2,
708 ConstructorScope = 1 << 3,
713 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
714 // it's public so that we can use a struct at the callsite
715 public struct FlagsHandle : IDisposable
717 readonly BuilderContext ec;
718 readonly Options invmask, oldval;
720 public FlagsHandle (BuilderContext ec, Options flagsToSet)
721 : this (ec, flagsToSet, flagsToSet)
725 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
729 oldval = ec.flags & mask;
730 ec.flags = (ec.flags & invmask) | (val & mask);
733 public void Dispose ()
735 ec.flags = (ec.flags & invmask) | oldval;
739 protected Options flags;
741 public bool HasSet (Options options)
743 return (this.flags & options) == options;
746 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
747 public FlagsHandle With (Options options, bool enable)
749 return new FlagsHandle (this, options, enable ? options : 0);
754 // Parser session objects. We could recreate all these objects for each parser
755 // instance but the best parser performance the session object can be reused
757 public class ParserSession
761 public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
762 public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
763 public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
764 public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
765 public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
767 public LocationsBag LocationsBag { get; set; }
768 public bool UseJayGlobalArrays { get; set; }
769 public LocatedToken[] LocatedTokens { get; set; }
771 public MD5 GetChecksumAlgorithm ()
773 return md5 ?? (md5 = MD5.Create ());