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