2002-08-12 Martin Baulig <martin@gnome.org>
[mono.git] / mcs / mcs / ecore.cs
index f02bf11d1b3c17db02d123fc1294ec8792ad9ff4..a7e2a1d8e0bacec06d3786b1f0d505d77e571849 100755 (executable)
@@ -54,7 +54,14 @@ namespace Mono.CSharp {
                // Allows SimpleNames to be returned.
                // This is used by MemberAccess to construct long names that can not be
                // partially resolved (namespace-qualified names for example).
-               SimpleName              = 8
+               SimpleName              = 8,
+
+               // Mask of all the expression class flags.
+               MaskExprClass           = 15,
+
+               // Disable control flow analysis while resolving the expression.
+               // This is used when resolving the instance expression of a field expression.
+               DisableFlowAnalysis     = 16
        }
 
        //
@@ -85,6 +92,42 @@ namespace Mono.CSharp {
                void AddressOf (EmitContext ec, AddressOp mode);
        }
 
+       /// <summary>
+       ///   This interface is implemented by variables
+       /// </summary>
+       public interface IVariable {
+               /// <summary>
+               ///   Checks whether the variable has already been assigned at
+               ///   the current position of the method's control flow and
+               ///   reports an appropriate error message if not.
+               ///
+               ///   If the variable is a struct, then this call checks whether
+               ///   all of its fields (including all private ones) have been
+               ///   assigned.
+               /// </summary>
+               bool IsAssigned (EmitContext ec, Location loc);
+
+               /// <summary>
+               ///   Checks whether field `name' in this struct has been assigned.
+               /// </summary>
+               bool IsFieldAssigned (EmitContext ec, string name, Location loc);
+
+               /// <summary>
+               ///   Tells the flow analysis code that the variable has already
+               ///   been assigned at the current code position.
+               ///
+               ///   If the variable is a struct, this call marks all its fields
+               ///   (including private fields) as being assigned.
+               /// </summary>
+               void SetAssigned (EmitContext ec);
+
+               /// <summary>
+               ///   Tells the flow analysis code that field `name' in this struct
+               ///   has already been assigned atthe current code position.
+               /// </summary>
+               void SetFieldAssigned (EmitContext ec, string name);
+       }
+
        /// <summary>
        ///   This interface denotes an expression which evaluates to a member
        ///   of a struct or a class.
@@ -121,6 +164,17 @@ namespace Mono.CSharp {
                }
        }
 
+       /// <summary>
+       ///   Expression which resolves to a type.
+       /// </summary>
+       public interface ITypeExpression
+       {
+               /// <summary>
+               ///   Resolve the expression, but only lookup types.
+               /// </summary>
+               Expression DoResolveType (EmitContext ec);
+       }
+
        /// <remarks>
        ///   Base class for expressions
        /// </remarks>
@@ -228,13 +282,28 @@ namespace Mono.CSharp {
                /// </remarks>
                public Expression Resolve (EmitContext ec, ResolveFlags flags)
                {
-                       Expression e;
+                       // Are we doing a types-only search ?
+                       if ((flags & ResolveFlags.MaskExprClass) == ResolveFlags.Type) {
+                               ITypeExpression type_expr = this as ITypeExpression;
+
+                               if (type_expr == null)
+                                       return null;
+
+                               return type_expr.DoResolveType (ec);
+                       }
+
+                       bool old_do_flow_analysis = ec.DoFlowAnalysis;
+                       if ((flags & ResolveFlags.DisableFlowAnalysis) != 0)
+                               ec.DoFlowAnalysis = false;
 
+                       Expression e;
                        if (this is SimpleName)
                                e = ((SimpleName) this).DoResolveAllowStatic (ec);
                        else 
                                e = DoResolve (ec);
 
+                       ec.DoFlowAnalysis = old_do_flow_analysis;
+
                        if (e == null)
                                return null;
 
@@ -469,7 +538,14 @@ namespace Mono.CSharp {
                public static Expression MemberLookup (EmitContext ec, Type t, string name,
                                                       MemberTypes mt, BindingFlags bf, Location loc)
                {
-                       MemberInfo [] mi = TypeManager.MemberLookup (ec.ContainerType, t, mt, bf, name);
+                       return MemberLookup (ec, ec.ContainerType, t, name, mt, bf, loc);
+               }
+
+               public static Expression MemberLookup (EmitContext ec, Type invocation_type, Type t,
+                                                      string name, MemberTypes mt, BindingFlags bf,
+                                                      Location loc)
+               {
+                       MemberInfo [] mi = TypeManager.MemberLookup (invocation_type, t, mt, bf, name);
 
                        if (mi == null)
                                return null;
@@ -500,12 +576,14 @@ namespace Mono.CSharp {
 
                public static Expression MemberLookup (EmitContext ec, Type t, string name, Location loc)
                {
-                       return MemberLookup (ec, t, name, AllMemberTypes, AllBindingFlags, loc);
+                       return MemberLookup (ec, ec.ContainerType, t, name,
+                                            AllMemberTypes, AllBindingFlags, loc);
                }
 
                public static Expression MethodLookup (EmitContext ec, Type t, string name, Location loc)
                {
-                       return MemberLookup (ec, t, name, MemberTypes.Method, AllBindingFlags, loc);
+                       return MemberLookup (ec, ec.ContainerType, t, name,
+                                            MemberTypes.Method, AllBindingFlags, loc);
                }
 
                /// <summary>
@@ -527,7 +605,7 @@ namespace Mono.CSharp {
 
                        int errors = Report.Errors;
 
-                       e = MemberLookup (ec, t, name, mt, bf, loc);
+                       e = MemberLookup (ec, ec.ContainerType, t, name, mt, bf, loc);
 
                        if (e != null)
                                return e;
@@ -3274,7 +3352,7 @@ namespace Mono.CSharp {
        ///   The downside of this is that we might be hitting `LookupType' too many
        ///   times with this scheme.
        /// </remarks>
-       public class SimpleName : Expression {
+       public class SimpleName : Expression, ITypeExpression {
                public readonly string Name;
                
                public SimpleName (string name, Location l)
@@ -3331,6 +3409,47 @@ namespace Mono.CSharp {
                        return SimpleNameResolve (ec, null, true);
                }
 
+               public Expression DoResolveType (EmitContext ec)
+               {
+                       //
+                       // Stage 3: Lookup symbol in the various namespaces. 
+                       //
+                       DeclSpace ds = ec.DeclSpace;
+                       Type t;
+                       string alias_value;
+
+                       if ((t = RootContext.LookupType (ds, Name, true, loc)) != null)
+                               return new TypeExpr (t, loc);
+                               
+                       //
+                       // Stage 2 part b: Lookup up if we are an alias to a type
+                       // or a namespace.
+                       //
+                       // Since we are cheating: we only do the Alias lookup for
+                       // namespaces if the name does not include any dots in it
+                       //
+                               
+                       alias_value = ec.DeclSpace.LookupAlias (Name);
+                               
+                       if (Name.IndexOf ('.') == -1 && alias_value != null) {
+                               if ((t = RootContext.LookupType (ds, alias_value, true, loc)) != null)
+                                       return new TypeExpr (t, loc);
+                                       
+                               // we have alias value, but it isn't Type, so try if it's namespace
+                               return new SimpleName (alias_value, loc);
+                       }
+                               
+                       if (ec.ResolvingTypeTree){
+                               Type dt = ec.DeclSpace.FindType (Name);
+                               if (dt != null)
+                                       return new TypeExpr (dt, loc);
+                       }
+
+                       // No match, maybe our parent can compose us
+                       // into something meaningful.
+                       return this;
+               }
+
                /// <remarks>
                ///   7.5.2: Simple Names. 
                ///
@@ -3355,122 +3474,94 @@ namespace Mono.CSharp {
                        //
                        // Stage 1: Performed by the parser (binding to locals or parameters).
                        //
-                       if (!ec.OnlyLookupTypes){
-                               Block current_block = ec.CurrentBlock;
-                               if (current_block != null && current_block.IsVariableDefined (Name)){
-                                       LocalVariableReference var;
-                                       
-                                       var = new LocalVariableReference (ec.CurrentBlock, Name, loc);
+                       Block current_block = ec.CurrentBlock;
+                       if (current_block != null && current_block.IsVariableDefined (Name)){
+                               LocalVariableReference var;
 
-                                       if (right_side != null)
-                                               return var.ResolveLValue (ec, right_side);
-                                       else
-                                               return var.Resolve (ec);
-                               }
+                               var = new LocalVariableReference (ec.CurrentBlock, Name, loc);
 
-                               if (current_block != null){
-                                       int idx = -1;
-                                       Parameter par = null;
-                                       Parameters pars = current_block.Parameters;
-                                       if (pars != null)
-                                               par = pars.GetParameterByName (Name, out idx);
+                               if (right_side != null)
+                                       return var.ResolveLValue (ec, right_side);
+                               else
+                                       return var.Resolve (ec);
+                       }
 
-                                       if (par != null) {
-                                               ParameterReference param;
+                       if (current_block != null){
+                               int idx = -1;
+                               Parameter par = null;
+                               Parameters pars = current_block.Parameters;
+                               if (pars != null)
+                                       par = pars.GetParameterByName (Name, out idx);
+
+                               if (par != null) {
+                                       ParameterReference param;
                                        
-                                               param = new ParameterReference (pars, idx, Name, loc);
+                                       param = new ParameterReference (pars, idx, Name, loc);
 
-                                               if (right_side != null)
-                                                       return param.ResolveLValue (ec, right_side);
-                                               else
-                                                       return param.Resolve (ec);
-                                       }
+                                       if (right_side != null)
+                                               return param.ResolveLValue (ec, right_side);
+                                       else
+                                               return param.Resolve (ec);
                                }
+                       }
 
-                               //
-                               // Stage 2: Lookup members 
-                               //
-
-                               //
-                               // For enums, the TypeBuilder is not ec.DeclSpace.TypeBuilder
-                               // Hence we have two different cases
-                               //
+                       //
+                       // Stage 2: Lookup members 
+                       //
 
-                               DeclSpace lookup_ds = ec.DeclSpace;
-                               do {
-                                       if (lookup_ds.TypeBuilder == null)
-                                               break;
+                       //
+                       // For enums, the TypeBuilder is not ec.DeclSpace.TypeBuilder
+                       // Hence we have two different cases
+                       //
 
-                                       e = MemberLookup (ec, lookup_ds.TypeBuilder, Name, loc);
-                                       if (e != null)
-                                               break;
+                       DeclSpace lookup_ds = ec.DeclSpace;
+                       do {
+                               if (lookup_ds.TypeBuilder == null)
+                                       break;
 
-                                       //
-                                       // Classes/structs keep looking, enums break
-                                       //
-                                       if (lookup_ds is TypeContainer)
-                                               lookup_ds = ((TypeContainer) lookup_ds).Parent;
-                                       else
-                                               break;
-                               } while (lookup_ds != null);
-                               
-                               if (e == null && ec.ContainerType != null)
-                                       e = MemberLookup (ec, ec.ContainerType, Name, loc);
-                       }
+                               e = MemberLookup (ec, lookup_ds.TypeBuilder, Name, loc);
+                               if (e != null)
+                                       break;
 
-                       // Continuation of stage 2
-                       if (e == null){
                                //
-                               // Stage 3: Lookup symbol in the various namespaces. 
+                               // Classes/structs keep looking, enums break
                                //
-                               DeclSpace ds = ec.DeclSpace;
-                               Type t;
-                               string alias_value;
-
-                               if ((t = RootContext.LookupType (ds, Name, true, loc)) != null)
-                                       return new TypeExpr (t, loc);
-                               
-                               //
-                               // Stage 2 part b: Lookup up if we are an alias to a type
-                               // or a namespace.
-                               //
-                               // Since we are cheating: we only do the Alias lookup for
-                               // namespaces if the name does not include any dots in it
-                               //
-                               
-                               alias_value = ec.DeclSpace.LookupAlias (Name);
-                               
-                               if (Name.IndexOf ('.') == -1 && alias_value != null) {
-                                       if ((t = RootContext.LookupType (ds, alias_value, true, loc))
-                                           != null)
-                                               return new TypeExpr (t, loc);
-                                       
-                               // we have alias value, but it isn't Type, so try if it's namespace
-                                       return new SimpleName (alias_value, loc);
-                               }
-                               
-                               if (ec.ResolvingTypeTree){
-                                       Type dt = ec.DeclSpace.FindType (Name);
-                                       if (dt != null)
-                                               return new TypeExpr (dt, loc);
-                               }
+                               if (lookup_ds is TypeContainer)
+                                       lookup_ds = ((TypeContainer) lookup_ds).Parent;
+                               else
+                                       break;
+                       } while (lookup_ds != null);
                                
-                               // No match, maybe our parent can compose us
-                               // into something meaningful.
-                               return this;
-                       }
+                       if (e == null && ec.ContainerType != null)
+                               e = MemberLookup (ec, ec.ContainerType, Name, loc);
+
+                       if (e == null)
+                               return DoResolveType (ec);
 
-                       //
-                       // Stage 2 continues here. 
-                       // 
                        if (e is TypeExpr)
                                return e;
 
-                       if (ec.OnlyLookupTypes)
-                               return null;
+                       if (e is IMemberExpr) {
+                               e = MemberAccess.ResolveMemberAccess (ec, e, null, loc, this);
+                               if (e == null)
+                                       return null;
 
-                       if (e is IMemberExpr)
-                               return MemberAccess.ResolveMemberAccess (ec, e, null, loc, this);
+                               IMemberExpr me = e as IMemberExpr;
+                               if (me == null)
+                                       return e;
+
+                               // This fails if ResolveMemberAccess() was unable to decide whether
+                               // it's a field or a type of the same name.
+                               if (!me.IsStatic && (me.InstanceExpression == null))
+                                       return e;
+
+                               if (right_side != null)
+                                       e = e.DoResolveLValue (ec, right_side);
+                               else
+                                       e = e.DoResolve (ec);
+
+                               return e;                               
+                       }
 
                        if (ec.IsStatic || ec.IsFieldInitializer){
                                if (allow_static)
@@ -3502,7 +3593,7 @@ namespace Mono.CSharp {
        /// <summary>
        ///   Fully resolved expression that evaluates to a type
        /// </summary>
-       public class TypeExpr : Expression {
+       public class TypeExpr : Expression, ITypeExpression {
                public TypeExpr (Type t, Location l)
                {
                        Type = t;
@@ -3510,6 +3601,11 @@ namespace Mono.CSharp {
                        loc = l;
                }
 
+               public virtual Expression DoResolveType (EmitContext ec)
+               {
+                       return this;
+               }
+
                override public Expression DoResolve (EmitContext ec)
                {
                        return this;
@@ -3523,27 +3619,32 @@ namespace Mono.CSharp {
 
        /// <summary>
        ///   Used to create types from a fully qualified name.  These are just used
-       ///   by the parser to setup the core types.  A TypeExpression is always
+       ///   by the parser to setup the core types.  A TypeLookupExpression is always
        ///   classified as a type.
        /// </summary>
-       public class TypeExpression : TypeExpr {
+       public class TypeLookupExpression : TypeExpr {
                string name;
                
-               public TypeExpression (string name) : base (null, Location.Null)
+               public TypeLookupExpression (string name) : base (null, Location.Null)
                {
                        this.name = name;
                }
 
-               public override Expression DoResolve (EmitContext ec)
+               public override Expression DoResolveType (EmitContext ec)
                {
                        if (type == null)
                                type = RootContext.LookupType (ec.DeclSpace, name, false, Location.Null);
                        return this;
                }
 
+               public override Expression DoResolve (EmitContext ec)
+               {
+                       return DoResolveType (ec);
+               }
+
                public override void Emit (EmitContext ec)
                {
-                       throw new Exception ("Should never be called");                 
+                       throw new Exception ("Should never be called");
                }
 
                public override string ToString ()
@@ -3734,11 +3835,20 @@ namespace Mono.CSharp {
                                                             "Of the FieldExpr to set this\n");
                                }
 
-                               instance_expr = instance_expr.Resolve (ec);
+                               // Resolve the field's instance expression while flow analysis is turned
+                               // off: when accessing a field "a.b", we must check whether the field
+                               // "a.b" is initialized, not whether the whole struct "a" is initialized.
+                               instance_expr = instance_expr.Resolve (ec, ResolveFlags.VariableOrValue |
+                                                                      ResolveFlags.DisableFlowAnalysis);
                                if (instance_expr == null)
                                        return null;
                        }
 
+                       // If the instance expression is a local variable or parameter.
+                       IVariable var = instance_expr as IVariable;
+                       if ((var != null) && !var.IsFieldAssigned (ec, FieldInfo.Name, loc))
+                               return null;
+
                        return this;
                }
 
@@ -3758,6 +3868,10 @@ namespace Mono.CSharp {
                
                override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
                {
+                       IVariable var = instance_expr as IVariable;
+                       if (var != null)
+                               var.SetFieldAssigned (ec, FieldInfo.Name);
+
                        Expression e = DoResolve (ec);
 
                        if (e == null)
@@ -3782,7 +3896,7 @@ namespace Mono.CSharp {
                {
                        ILGenerator ig = ec.ig;
                        bool is_volatile = false;
-                               
+
                        if (FieldInfo is FieldBuilder){
                                FieldBase f = TypeManager.GetField (FieldInfo);
 
@@ -4009,7 +4123,18 @@ namespace Mono.CSharp {
                                return null;
                        }
 
-                       type = PropertyInfo.PropertyType;
+                       return this;
+               }
+
+               override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
+               {
+                       if (!PropertyInfo.CanWrite){
+                               Report.Error (154, loc, 
+                                             "The property `" + PropertyInfo.Name +
+                                             "' can not be used in " +
+                                             "this context because it lacks a set accessor");
+                               return null;
+                       }
 
                        return this;
                }