2002-05-28 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / mcs / assign.cs
1 //
2 // assign.cs: Assignments.
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2001, 2002 Ximian, Inc.
8 //
9 using System;
10 using System.Reflection;
11 using System.Reflection.Emit;
12
13 namespace Mono.CSharp {
14
15         /// <summary>
16         ///   This interface is implemented by expressions that can be assigned to.
17         /// </summary>
18         /// <remarks>
19         ///   This interface is implemented by Expressions whose values can not
20         ///   store the result on the top of the stack.
21         ///
22         ///   Expressions implementing this (Properties, Indexers and Arrays) would
23         ///   perform an assignment of the Expression "source" into its final
24         ///   location.
25         ///
26         ///   No values on the top of the stack are expected to be left by
27         ///   invoking this method.
28         /// </remarks>
29         public interface IAssignMethod {
30                 //
31                 // This method will emit the code for the actual assignment
32                 //
33                 void EmitAssign (EmitContext ec, Expression source);
34
35                 //
36                 // This method is invoked before any code generation takes
37                 // place, and it is a mechanism to inform that the expression
38                 // will be invoked more than once, and that the method should
39                 // use temporary values to avoid having side effects
40                 //
41                 // Example: a [ g () ] ++
42                 //
43                 void CacheTemporaries (EmitContext ec);
44         }
45
46         /// <summary>
47         ///   An Expression to hold a temporary value.
48         /// </summary>
49         /// <remarks>
50         ///   The LocalTemporary class is used to hold temporary values of a given
51         ///   type to "simulate" the expression semantics on property and indexer
52         ///   access whose return values are void.
53         ///
54         ///   The local temporary is used to alter the normal flow of code generation
55         ///   basically it creates a local variable, and its emit instruction generates
56         ///   code to access this value, return its address or save its value.
57         /// </remarks>
58         public class LocalTemporary : Expression, IMemoryLocation {
59                 LocalBuilder builder;
60                 
61                 public LocalTemporary (EmitContext ec, Type t)
62                 {
63                         type = t;
64                         eclass = ExprClass.Value;
65                         builder = ec.GetTemporaryStorage (t);
66                 }
67
68                 public void Release (EmitContext ec)
69                 {
70                         ec.FreeTemporaryStorage (builder);
71                         builder = null;
72                 }
73                 
74                 public LocalTemporary (LocalBuilder b, Type t)
75                 {
76                         type = t;
77                         eclass = ExprClass.Value;
78                         builder = b;
79                 }
80                 
81                 public override Expression DoResolve (EmitContext ec)
82                 {
83                         return this;
84                 }
85
86                 public override void Emit (EmitContext ec)
87                 {
88                         ec.ig.Emit (OpCodes.Ldloc, builder); 
89                 }
90
91                 public void Store (EmitContext ec)
92                 {
93                         ec.ig.Emit (OpCodes.Stloc, builder);
94                 }
95
96                 public void AddressOf (EmitContext ec, AddressOp mode)
97                 {
98                         ec.ig.Emit (OpCodes.Ldloca, builder);
99                 }
100         }
101
102         /// <summary>
103         ///   The Assign node takes care of assigning the value of source into
104         ///   the expression represented by target. 
105         /// </summary>
106         public class Assign : ExpressionStatement {
107                 protected Expression target, source;
108                 public Location l;
109
110                 public Assign (Expression target, Expression source, Location l)
111                 {
112                         this.target = target;
113                         this.source = source;
114                         this.l = l;
115                 }
116
117                 public Expression Target {
118                         get {
119                                 return target;
120                         }
121
122                         set {
123                                 target = value;
124                         }
125                 }
126
127                 public Expression Source {
128                         get {
129                                 return source;
130                         }
131
132                         set {
133                                 source = value;
134                         }
135                 }
136
137                 public static void error70 (EventInfo ei, Location l)
138                 {
139                         Report.Error (70, l, "The event '" + ei.Name +
140                                       "' can only appear on the left-side of a += or -= (except when" +
141                                       " used from within the type '" + ei.DeclaringType + "')");
142                 }
143
144                 //
145                 // Will return either `this' or an instance of `New'.
146                 //
147                 public override Expression DoResolve (EmitContext ec)
148                 {
149                         source = source.Resolve (ec);
150                         if (source == null)
151                                 return null;
152
153                         target = target.ResolveLValue (ec, source);
154                         
155                         if (target == null)
156                                 return null;
157
158                         Type target_type = target.Type;
159                         Type source_type = source.Type;
160
161                         type = target_type;
162                         eclass = ExprClass.Value;
163
164                         //
165                         // If we are doing a property assignment, then
166                         // set the `value' field on the property, and Resolve
167                         // it.
168                         //
169                         if (target is PropertyExpr){
170                                 PropertyExpr property_assign = (PropertyExpr) target;
171
172                                 if (source_type != target_type){
173                                         source = ConvertImplicitRequired (ec, source, target_type, l);
174                                         if (source == null)
175                                                 return null;
176                                 }
177
178                                 //
179                                 // FIXME: Maybe handle this in the LValueResolve
180                                 //
181                                 if (!property_assign.VerifyAssignable ())
182                                         return null;
183                                 
184                                 return this;
185                         }
186
187                         if (target is IndexerAccess){
188                                 if (source_type != target_type){
189                                         source = ConvertImplicitRequired (ec, source, target_type, l);
190                                         if (source == null)
191                                                 return null;
192                                 }
193                                 
194                                 return this;
195                         }
196
197                         if (target is EventExpr) {
198
199                                 Binary tmp;
200                                 EventInfo ei = ((EventExpr) target).EventInfo;
201
202
203                                 Expression ml = MemberLookup (
204                                         ec, ec.ContainerType, ei.Name,
205                                         MemberTypes.Event, AllBindingFlags, l);
206
207                                 if (ml == null) {
208                                         //
209                                         // If this is the case, then the Event does not belong 
210                                         // to this TypeContainer and so, according to the spec
211                                         // is allowed to only appear on the left hand of
212                                         // the += and -= operators
213                                         //
214                                         // Note that if target will not appear as an EventExpr
215                                         // in the case it is being referenced within the same type container;
216                                         // it will appear as a FieldExpr in that case.
217                                         //
218                                         
219                                         if (!(source is Binary)) {
220                                                 error70 (ei, l);
221                                                 return null;
222                                         } else {
223                                                 tmp = ((Binary) source);
224                                                 if (tmp.Oper != Binary.Operator.Addition &&
225                                                     tmp.Oper != Binary.Operator.Subtraction) {
226                                                         error70 (ei, l);
227                                                         return null;
228                                                 }
229                                         }
230                                 }
231                         }
232                         
233                         if (source is New && target_type.IsSubclassOf (TypeManager.value_type)){
234                                 New n = (New) source;
235
236                                 n.ValueTypeVariable = target;
237                                 return n;
238                         }
239
240                         if (target.eclass != ExprClass.Variable && target.eclass != ExprClass.EventAccess){
241                                 Report.Error (131, l,
242                                               "Left hand of an assignment must be a variable, " +
243                                               "a property or an indexer");
244                                 return null;
245                         }
246
247                         if (target_type == source_type)
248                                 return this;
249                         
250                         //
251                         // If this assignemnt/operator was part of a compound binary
252                         // operator, then we allow an explicit conversion, as detailed
253                         // in the spec. 
254                         //
255
256                         if (this is CompoundAssign){
257                                 CompoundAssign a = (CompoundAssign) this;
258                                 
259                                 Binary b = source as Binary;
260                                 if (b != null && b.IsBuiltinOperator){
261                                         //
262                                         // 1. if the source is explicitly convertible to the
263                                         //    target_type
264                                         //
265                                         
266                                         source = ConvertExplicit (ec, source, target_type, l);
267                                         if (source == null){
268                                                 Error_CannotConvertImplicit (l, source_type, target_type);
269                                                 return null;
270                                         }
271                                 
272                                         //
273                                         // 2. and the original right side is implicitly convertible to
274                                         // the type of target_type.
275                                         //
276                                         if (StandardConversionExists (a.original_source, target_type))
277                                                 return this;
278
279                                         Error_CannotConvertImplicit (l, a.original_source.Type, target_type);
280                                         return null;
281                                 }
282                         }
283                         
284                         source = ConvertImplicitRequired (ec, source, target_type, l);
285                         if (source == null)
286                                 return null;
287
288                         return this;
289                 }
290
291                 void Emit (EmitContext ec, bool is_statement)
292                 {
293                         if (target is EventExpr) {
294                                 ((EventExpr) target).EmitAddOrRemove (ec, source);
295                                 return;
296                         }
297
298                         //
299                         // FIXME! We need a way to "probe" if the process can
300                         // just use `dup' to propagate the result
301                         // 
302                         IAssignMethod am = (IAssignMethod) target;
303
304                         if (this is CompoundAssign){
305                                 am.CacheTemporaries (ec);
306                         }
307                         
308                         if (is_statement)
309                                 am.EmitAssign (ec, source);
310                         else {
311                                 LocalTemporary tempo;
312                                 
313                                 tempo = new LocalTemporary (ec, source.Type);
314                                 
315                                 source.Emit (ec);
316                                 tempo.Store (ec);
317                                 am.EmitAssign (ec, tempo);
318                                 tempo.Emit (ec);
319                                 tempo.Release (ec);
320                         }
321                 }
322                 
323                 public override void Emit (EmitContext ec)
324                 {
325                         Emit (ec, false);
326                 }
327
328                 public override void EmitStatement (EmitContext ec)
329                 {
330                         Emit (ec, true);
331                 }
332         }
333
334         
335         //
336         // This class is used for compound assignments.  
337         //
338         class CompoundAssign : Assign {
339                 Binary.Operator op;
340                 public Expression original_source;
341                 
342                 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location l)
343                         : base (target, source, l)
344                 {
345                         original_source = source;
346                         this.op = op;
347                 }
348
349                 public Expression ResolveSource (EmitContext ec)
350                 {
351                         return original_source.Resolve (ec);
352                 }
353
354                 public override Expression DoResolve (EmitContext ec)
355                 {
356                         target = target.ResolveLValue (ec, source);
357                         if (target == null)
358                                 return null;
359
360                         original_source = original_source.Resolve (ec);
361                         if (original_source == null)
362                                 return null;
363
364                         //
365                         // Only now we can decouple the original source/target
366                         // into a tree, to guarantee that we do not have side
367                         // effects.
368                         //
369                         source = new Binary (op, target, original_source, l);
370                         return base.DoResolve (ec);
371                 }
372         }
373 }
374
375
376
377