Enable await expressions to work with dynamic binder
[mono.git] / mcs / mcs / context.cs
1 //
2 // context.cs: Various compiler contexts.
3 //
4 // Author:
5 //   Marek Safar (marek.safar@gmail.com)
6 //   Miguel de Icaza (miguel@ximian.com)
7 //
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2004-2009 Novell, Inc.
10 //
11
12 using System;
13 using System.Collections.Generic;
14 using System.IO;
15
16 namespace Mono.CSharp
17 {
18         public enum LookupMode
19         {
20                 Normal = 0,
21                 Probing = 1,
22                 IgnoreAccessibility = 2
23         }
24
25         //
26         // Implemented by elements which can act as independent contexts
27         // during resolve phase. Used mostly for lookups.
28         //
29         public interface IMemberContext : IModuleContext
30         {
31                 //
32                 // A scope type context, it can be inflated for generic types
33                 //
34                 TypeSpec CurrentType { get; }
35
36                 //
37                 // A scope type parameters either VAR or MVAR
38                 //
39                 TypeParameter[] CurrentTypeParameters { get; }
40
41                 //
42                 // A member definition of the context. For partial types definition use
43                 // CurrentTypeDefinition.PartialContainer otherwise the context is local
44                 //
45                 // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value
46                 //
47                 MemberCore CurrentMemberDefinition { get; }
48
49                 bool IsObsolete { get; }
50                 bool IsUnsafe { get; }
51                 bool IsStatic { get; }
52
53                 string GetSignatureForError ();
54
55                 ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity);
56                 FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc);
57                 FullNamedExpression LookupNamespaceAlias (string name);
58         }
59
60         public interface IModuleContext
61         {
62                 ModuleContainer Module { get; }
63         }
64
65         //
66         // Block or statement resolving context
67         //
68         public class BlockContext : ResolveContext
69         {
70                 FlowBranching current_flow_branching;
71
72                 readonly TypeSpec return_type;
73
74                 public int FlowOffset;
75
76                 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
77                         : base (mc)
78                 {
79                         if (returnType == null)
80                                 throw new ArgumentNullException ("returnType");
81
82                         this.return_type = returnType;
83
84                         // TODO: check for null value
85                         CurrentBlock = block;
86                 }
87
88                 public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
89                         : this (rc.MemberContext, block, returnType)
90                 {
91                         if (rc.IsUnsafe)
92                                 flags |= ResolveContext.Options.UnsafeScope;
93
94                         if (rc.HasSet (ResolveContext.Options.CheckedScope))
95                                 flags |= ResolveContext.Options.CheckedScope;
96                 }
97
98                 public override FlowBranching CurrentBranching {
99                         get { return current_flow_branching; }
100                 }
101
102                 public TypeSpec ReturnType {
103                         get { return return_type; }
104                 }
105
106                 // <summary>
107                 //   Starts a new code branching.  This inherits the state of all local
108                 //   variables and parameters from the current branching.
109                 // </summary>
110                 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
111                 {
112                         current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
113                         return current_flow_branching;
114                 }
115
116                 // <summary>
117                 //   Starts a new code branching for block `block'.
118                 // </summary>
119                 public FlowBranching StartFlowBranching (Block block)
120                 {
121                         Set (Options.DoFlowAnalysis);
122
123                         current_flow_branching = FlowBranching.CreateBranching (
124                                 CurrentBranching, FlowBranching.BranchingType.Block, block, block.StartLocation);
125                         return current_flow_branching;
126                 }
127
128                 public FlowBranchingTryCatch StartFlowBranching (TryCatch stmt)
129                 {
130                         FlowBranchingTryCatch branching = new FlowBranchingTryCatch (CurrentBranching, stmt);
131                         current_flow_branching = branching;
132                         return branching;
133                 }
134
135                 public FlowBranchingTryFinally StartFlowBranching (TryFinallyBlock stmt)
136                 {
137                         FlowBranchingTryFinally branching = new FlowBranchingTryFinally (CurrentBranching, stmt);
138                         current_flow_branching = branching;
139                         return branching;
140                 }
141
142                 public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt)
143                 {
144                         FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt);
145                         current_flow_branching = branching;
146                         return branching;
147                 }
148
149                 public FlowBranchingIterator StartFlowBranching (Iterator iterator, FlowBranching parent)
150                 {
151                         FlowBranchingIterator branching = new FlowBranchingIterator (parent, iterator);
152                         current_flow_branching = branching;
153                         return branching;
154                 }
155
156                 public FlowBranchingAsync StartFlowBranching (AsyncInitializer asyncBody, FlowBranching parent)
157                 {
158                         var branching = new FlowBranchingAsync (parent, asyncBody);
159                         current_flow_branching = branching;
160                         return branching;
161                 }
162
163                 public FlowBranchingToplevel StartFlowBranching (ParametersBlock stmt, FlowBranching parent)
164                 {
165                         FlowBranchingToplevel branching = new FlowBranchingToplevel (parent, stmt);
166                         current_flow_branching = branching;
167                         return branching;
168                 }
169
170                 // <summary>
171                 //   Ends a code branching.  Merges the state of locals and parameters
172                 //   from all the children of the ending branching.
173                 // </summary>
174                 public bool EndFlowBranching ()
175                 {
176                         FlowBranching old = current_flow_branching;
177                         current_flow_branching = current_flow_branching.Parent;
178
179                         FlowBranching.UsageVector vector = current_flow_branching.MergeChild (old);
180                         return vector.IsUnreachable;
181                 }
182
183                 // <summary>
184                 //   Kills the current code branching.  This throws away any changed state
185                 //   information and should only be used in case of an error.
186                 // </summary>
187                 // FIXME: this is evil
188                 public void KillFlowBranching ()
189                 {
190                         current_flow_branching = current_flow_branching.Parent;
191                 }
192
193 #if !STATIC
194                 public void NeedReturnLabel ()
195                 {
196                 }
197 #endif
198         }
199
200         //
201         // Expression resolving context
202         //
203         public class ResolveContext : IMemberContext
204         {
205                 [Flags]
206                 public enum Options
207                 {
208                         /// <summary>
209                         ///   This flag tracks the `checked' state of the compilation,
210                         ///   it controls whether we should generate code that does overflow
211                         ///   checking, or if we generate code that ignores overflows.
212                         ///
213                         ///   The default setting comes from the command line option to generate
214                         ///   checked or unchecked code plus any source code changes using the
215                         ///   checked/unchecked statements or expressions.   Contrast this with
216                         ///   the ConstantCheckState flag.
217                         /// </summary>
218                         CheckedScope = 1 << 0,
219
220                         /// <summary>
221                         ///   The constant check state is always set to `true' and cant be changed
222                         ///   from the command line.  The source code can change this setting with
223                         ///   the `checked' and `unchecked' statements and expressions. 
224                         /// </summary>
225                         ConstantCheckState = 1 << 1,
226
227                         AllCheckStateFlags = CheckedScope | ConstantCheckState,
228
229                         //
230                         // unsafe { ... } scope
231                         //
232                         UnsafeScope = 1 << 2,
233                         CatchScope = 1 << 3,
234                         FinallyScope = 1 << 4,
235                         FieldInitializerScope = 1 << 5,
236                         CompoundAssignmentScope = 1 << 6,
237                         FixedInitializerScope = 1 << 7,
238                         BaseInitializer = 1 << 8,
239
240                         //
241                         // Inside an enum definition, we do not resolve enumeration values
242                         // to their enumerations, but rather to the underlying type/value
243                         // This is so EnumVal + EnumValB can be evaluated.
244                         //
245                         // There is no "E operator + (E x, E y)", so during an enum evaluation
246                         // we relax the rules
247                         //
248                         EnumScope = 1 << 9,
249
250                         ConstantScope = 1 << 10,
251
252                         ConstructorScope = 1 << 11,
253
254                         UsingInitializerScope = 1 << 12,
255
256                         LockScope = 1 << 13,
257
258                         /// <summary>
259                         ///   Whether control flow analysis is enabled
260                         /// </summary>
261                         DoFlowAnalysis = 1 << 20,
262
263                         /// <summary>
264                         ///   Whether control flow analysis is disabled on structs
265                         ///   (only meaningful when DoFlowAnalysis is set)
266                         /// </summary>
267                         OmitStructFlowAnalysis = 1 << 21,
268
269                         ///
270                         /// Indicates the current context is in probing mode, no errors are reported. 
271                         ///
272                         ProbingMode = 1 << 22,
273
274                         //
275                         // Return and ContextualReturn statements will set the ReturnType
276                         // value based on the expression types of each return statement
277                         // instead of the method return type which is initially null.
278                         //
279                         InferReturnType = 1 << 23,
280
281                         OmitDebuggingInfo = 1 << 24,
282
283                         ExpressionTreeConversion = 1 << 25,
284
285                         InvokeSpecialName = 1 << 26
286                 }
287
288                 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
289                 // it's public so that we can use a struct at the callsite
290                 public struct FlagsHandle : IDisposable
291                 {
292                         ResolveContext ec;
293                         readonly Options invmask, oldval;
294
295                         public FlagsHandle (ResolveContext ec, Options flagsToSet)
296                                 : this (ec, flagsToSet, flagsToSet)
297                         {
298                         }
299
300                         internal FlagsHandle (ResolveContext ec, Options mask, Options val)
301                         {
302                                 this.ec = ec;
303                                 invmask = ~mask;
304                                 oldval = ec.flags & mask;
305                                 ec.flags = (ec.flags & invmask) | (val & mask);
306
307 //                              if ((mask & Options.ProbingMode) != 0)
308 //                                      ec.Report.DisableReporting ();
309                         }
310
311                         public void Dispose ()
312                         {
313 //                              if ((invmask & Options.ProbingMode) == 0)
314 //                                      ec.Report.EnableReporting ();
315
316                                 ec.flags = (ec.flags & invmask) | oldval;
317                         }
318                 }
319
320                 protected Options flags;
321
322                 //
323                 // Whether we are inside an anonymous method.
324                 //
325                 public AnonymousExpression CurrentAnonymousMethod;
326
327                 //
328                 // Holds a varible used during collection or object initialization.
329                 //
330                 public Expression CurrentInitializerVariable;
331
332                 public Block CurrentBlock;
333
334                 public readonly IMemberContext MemberContext;
335
336                 /// <summary>
337                 ///   If this is non-null, points to the current switch statement
338                 /// </summary>
339                 public Switch Switch;
340
341                 public ResolveContext (IMemberContext mc)
342                 {
343                         if (mc == null)
344                                 throw new ArgumentNullException ();
345
346                         MemberContext = mc;
347
348                         //
349                         // The default setting comes from the command line option
350                         //
351                         if (mc.Module.Compiler.Settings.Checked)
352                                 flags |= Options.CheckedScope;
353
354                         //
355                         // The constant check state is always set to true
356                         //
357                         flags |= Options.ConstantCheckState;
358                 }
359
360                 public ResolveContext (IMemberContext mc, Options options)
361                         : this (mc)
362                 {
363                         flags |= options;
364                 }
365
366                 #region Properties
367
368                 public BuiltinTypes BuiltinTypes {
369                         get {
370                                 return MemberContext.Module.Compiler.BuiltinTypes;
371                         }
372                 }
373
374                 public virtual ExplicitBlock ConstructorBlock {
375                         get {
376                                 return CurrentBlock.Explicit;
377                         }
378                 }
379
380                 public virtual FlowBranching CurrentBranching {
381                         get { return null; }
382                 }
383
384                 //
385                 // The current iterator
386                 //
387                 public Iterator CurrentIterator {
388                         get { return CurrentAnonymousMethod as Iterator; }
389                 }
390
391                 public TypeSpec CurrentType {
392                         get { return MemberContext.CurrentType; }
393                 }
394
395                 public TypeParameter[] CurrentTypeParameters {
396                         get { return MemberContext.CurrentTypeParameters; }
397                 }
398
399                 public MemberCore CurrentMemberDefinition {
400                         get { return MemberContext.CurrentMemberDefinition; }
401                 }
402
403                 public bool ConstantCheckState {
404                         get { return (flags & Options.ConstantCheckState) != 0; }
405                 }
406
407                 public bool DoFlowAnalysis {
408                         get { return (flags & Options.DoFlowAnalysis) != 0; }
409                 }
410
411                 public bool IsInProbingMode {
412                         get {
413                                 return (flags & Options.ProbingMode) != 0;
414                         }
415                 }
416
417                 public bool IsObsolete {
418                         get {
419                                 // Disables obsolete checks when probing is on
420                                 return MemberContext.IsObsolete;
421                         }
422                 }
423
424                 public bool IsStatic {
425                         get {
426                                 return MemberContext.IsStatic;
427                         }
428                 }
429
430                 public bool IsUnsafe {
431                         get {
432                                 return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
433                         }
434                 }
435
436                 public bool IsRuntimeBinder {
437                         get {
438                                 return Module.Compiler.IsRuntimeBinder;
439                         }
440                 }
441
442                 public bool IsVariableCapturingRequired {
443                         get {
444                                 return !IsInProbingMode && (CurrentBranching == null || !CurrentBranching.CurrentUsageVector.IsUnreachable);
445                         }
446                 }
447
448                 public ModuleContainer Module {
449                         get {
450                                 return MemberContext.Module;
451                         }
452                 }
453
454                 public bool OmitStructFlowAnalysis {
455                         get { return (flags & Options.OmitStructFlowAnalysis) != 0; }
456                 }
457
458                 public Report Report {
459                         get {
460                                 return Module.Compiler.Report;
461                         }
462                 }
463
464                 #endregion
465
466                 public bool MustCaptureVariable (INamedBlockVariable local)
467                 {
468                         if (CurrentAnonymousMethod == null)
469                                 return false;
470
471                         // FIXME: IsIterator is too aggressive, we should capture only if child
472                         // block contains yield
473                         if (CurrentAnonymousMethod.IsIterator || CurrentAnonymousMethod is AsyncInitializer)
474                                 return true;
475
476                         return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
477                 }
478
479                 public bool HasSet (Options options)
480                 {
481                         return (this.flags & options) == options;
482                 }
483
484                 public bool HasAny (Options options)
485                 {
486                         return (this.flags & options) != 0;
487                 }
488
489
490                 // Temporarily set all the given flags to the given value.  Should be used in an 'using' statement
491                 public FlagsHandle Set (Options options)
492                 {
493                         return new FlagsHandle (this, options);
494                 }
495
496                 public FlagsHandle With (Options options, bool enable)
497                 {
498                         return new FlagsHandle (this, options, enable ? options : 0);
499                 }
500
501                 #region IMemberContext Members
502
503                 public string GetSignatureForError ()
504                 {
505                         return MemberContext.GetSignatureForError ();
506                 }
507
508                 public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity)
509                 {
510                         return MemberContext.LookupExtensionMethod (extensionType, name, arity);
511                 }
512
513                 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
514                 {
515                         return MemberContext.LookupNamespaceOrType (name, arity, mode, loc);
516                 }
517
518                 public FullNamedExpression LookupNamespaceAlias (string name)
519                 {
520                         return MemberContext.LookupNamespaceAlias (name);
521                 }
522
523                 #endregion
524         }
525
526         //
527         // This class is used during the Statement.Clone operation
528         // to remap objects that have been cloned.
529         //
530         // Since blocks are cloned by Block.Clone, we need a way for
531         // expressions that must reference the block to be cloned
532         // pointing to the new cloned block.
533         //
534         public class CloneContext
535         {
536                 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
537
538                 public void AddBlockMap (Block from, Block to)
539                 {
540                         block_map.Add (from, to);
541                 }
542
543                 public Block LookupBlock (Block from)
544                 {
545                         Block result;
546                         if (!block_map.TryGetValue (from, out result)) {
547                                 result = (Block) from.Clone (this);
548                         }
549
550                         return result;
551                 }
552
553                 ///
554                 /// Remaps block to cloned copy if one exists.
555                 ///
556                 public Block RemapBlockCopy (Block from)
557                 {
558                         Block mapped_to;
559                         if (!block_map.TryGetValue (from, out mapped_to))
560                                 return from;
561
562                         return mapped_to;
563                 }
564         }
565
566         //
567         // Main compiler context
568         //
569         public class CompilerContext
570         {
571                 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
572
573                 readonly Report report;
574                 readonly BuiltinTypes builtin_types;
575                 readonly CompilerSettings settings;
576
577                 Dictionary<string, SourceFile> all_source_files;
578
579                 public CompilerContext (CompilerSettings settings, Report report)
580                 {
581                         this.settings = settings;
582                         this.report = report;
583                         this.builtin_types = new BuiltinTypes ();
584                         this.TimeReporter = DisabledTimeReporter;
585                 }
586
587                 #region Properties
588
589                 public BuiltinTypes BuiltinTypes {
590                         get {
591                                 return builtin_types;
592                         }
593                 }
594
595                 // Used for special handling of runtime dynamic context mostly
596                 // by error reporting but also by member accessibility checks
597                 public bool IsRuntimeBinder {
598                         get; set;
599                 }
600
601                 public Report Report {
602                         get {
603                                 return report;
604                         }
605                 }
606
607                 public CompilerSettings Settings {
608                         get {
609                                 return settings;
610                         }
611                 }
612
613                 public List<CompilationSourceFile> SourceFiles {
614                         get {
615                                 return settings.SourceFiles;
616                         }
617                 }
618
619                 internal TimeReporter TimeReporter {
620                         get; set;
621                 }
622
623                 #endregion
624
625                 //
626                 // This is used when we encounter a #line preprocessing directive during parsing
627                 // to register additional source file names
628                 //
629                 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
630                 {
631                         if (all_source_files == null) {
632                                 all_source_files = new Dictionary<string, SourceFile> ();
633                                 foreach (var source in SourceFiles)
634                                         all_source_files[source.FullPathName] = source;
635                         }
636
637                         string path;
638                         if (!Path.IsPathRooted (name)) {
639                                 string root = Path.GetDirectoryName (comp_unit.FullPathName);
640                                 path = Path.Combine (root, name);
641                         } else
642                                 path = name;
643
644                         SourceFile retval;
645                         if (all_source_files.TryGetValue (path, out retval))
646                                 return retval;
647
648                         retval = Location.AddFile (name, path);
649                         all_source_files.Add (path, retval);
650                         return retval;
651                 }
652         }
653
654         //
655         // Generic code emitter context
656         //
657         public class BuilderContext
658         {
659                 [Flags]
660                 public enum Options
661                 {
662                         /// <summary>
663                         ///   This flag tracks the `checked' state of the compilation,
664                         ///   it controls whether we should generate code that does overflow
665                         ///   checking, or if we generate code that ignores overflows.
666                         ///
667                         ///   The default setting comes from the command line option to generate
668                         ///   checked or unchecked code plus any source code changes using the
669                         ///   checked/unchecked statements or expressions.   Contrast this with
670                         ///   the ConstantCheckState flag.
671                         /// </summary>
672                         CheckedScope = 1 << 0,
673
674                         OmitDebugInfo = 1 << 2,
675
676                         ConstructorScope = 1 << 3,
677
678                         AsyncBody = 1 << 4
679                 }
680
681                 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
682                 // it's public so that we can use a struct at the callsite
683                 public struct FlagsHandle : IDisposable
684                 {
685                         BuilderContext ec;
686                         readonly Options invmask, oldval;
687
688                         public FlagsHandle (BuilderContext ec, Options flagsToSet)
689                                 : this (ec, flagsToSet, flagsToSet)
690                         {
691                         }
692
693                         internal FlagsHandle (BuilderContext ec, Options mask, Options val)
694                         {
695                                 this.ec = ec;
696                                 invmask = ~mask;
697                                 oldval = ec.flags & mask;
698                                 ec.flags = (ec.flags & invmask) | (val & mask);
699                         }
700
701                         public void Dispose ()
702                         {
703                                 ec.flags = (ec.flags & invmask) | oldval;
704                         }
705                 }
706
707                 protected Options flags;
708
709                 public bool HasSet (Options options)
710                 {
711                         return (this.flags & options) == options;
712                 }
713
714                 // Temporarily set all the given flags to the given value.  Should be used in an 'using' statement
715                 public FlagsHandle With (Options options, bool enable)
716                 {
717                         return new FlagsHandle (this, options, enable ? options : 0);
718                 }
719         }
720 }