2002-06-15 Rafael Teixeira <rafaelteixeirabr@hotmail.com>
[mono.git] / mcs / mbas / 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                                 return this;
189
190                         if (target is EventExpr) {
191
192                                 Binary tmp;
193                                 EventInfo ei = ((EventExpr) target).EventInfo;
194
195
196                                 Expression ml = MemberLookup (
197                                         ec, ec.ContainerType, ei.Name,
198                                         MemberTypes.Event, AllBindingFlags, l);
199
200                                 if (ml == null) {
201                                         //
202                                         // If this is the case, then the Event does not belong 
203                                         // to this TypeContainer and so, according to the spec
204                                         // is allowed to only appear on the left hand of
205                                         // the += and -= operators
206                                         //
207                                         // Note that if target will not appear as an EventExpr
208                                         // in the case it is being referenced within the same type container;
209                                         // it will appear as a FieldExpr in that case.
210                                         //
211                                         
212                                         if (!(source is Binary)) {
213                                                 error70 (ei, l);
214                                                 return null;
215                                         } else {
216                                                 tmp = ((Binary) source);
217                                                 if (tmp.Oper != Binary.Operator.Addition &&
218                                                     tmp.Oper != Binary.Operator.Subtraction) {
219                                                         error70 (ei, l);
220                                                         return null;
221                                                 }
222                                         }
223                                 }
224                         }
225                         
226                         if (source is New && target_type.IsSubclassOf (TypeManager.value_type)){
227                                 New n = (New) source;
228
229                                 n.ValueTypeVariable = target;
230                                 return n;
231                         }
232
233                         if (target.eclass != ExprClass.Variable && target.eclass != ExprClass.EventAccess){
234                                 Report.Error (131, l,
235                                               "Left hand of an assignment must be a variable, " +
236                                               "a property or an indexer");
237                                 return null;
238                         }
239
240                         if (target_type == source_type)
241                                 return this;
242                         
243                         //
244                         // If this assignemnt/operator was part of a compound binary
245                         // operator, then we allow an explicit conversion, as detailed
246                         // in the spec. 
247                         //
248
249                         if (this is CompoundAssign){
250                                 CompoundAssign a = (CompoundAssign) this;
251                                 
252                                 Binary b = source as Binary;
253                                 if (b != null && b.IsBuiltinOperator){
254                                         //
255                                         // 1. if the source is explicitly convertible to the
256                                         //    target_type
257                                         //
258                                         
259                                         source = ConvertExplicit (ec, source, target_type, l);
260                                         if (source == null){
261                                                 Error_CannotConvertImplicit (l, source_type, target_type);
262                                                 return null;
263                                         }
264                                 
265                                         //
266                                         // 2. and the original right side is implicitly convertible to
267                                         // the type of target_type.
268                                         //
269                                         if (StandardConversionExists (a.original_source, target_type))
270                                                 return this;
271
272                                         Error_CannotConvertImplicit (l, a.original_source.Type, target_type);
273                                         return null;
274                                 }
275                         }
276                         
277                         source = ConvertImplicitRequired (ec, source, target_type, l);
278                         if (source == null)
279                                 return null;
280
281                         return this;
282                 }
283
284                 void Emit (EmitContext ec, bool is_statement)
285                 {
286                         if (target is EventExpr) {
287                                 ((EventExpr) target).EmitAddOrRemove (ec, source);
288                                 return;
289                         }
290
291                         //
292                         // FIXME! We need a way to "probe" if the process can
293                         // just use `dup' to propagate the result
294                         // 
295                         IAssignMethod am = (IAssignMethod) target;
296
297                         if (this is CompoundAssign){
298                                 am.CacheTemporaries (ec);
299                         }
300                         
301                         if (is_statement)
302                                 am.EmitAssign (ec, source);
303                         else {
304                                 LocalTemporary tempo;
305                                 
306                                 tempo = new LocalTemporary (ec, source.Type);
307                                 
308                                 source.Emit (ec);
309                                 tempo.Store (ec);
310                                 am.EmitAssign (ec, tempo);
311                                 tempo.Emit (ec);
312                                 tempo.Release (ec);
313                         }
314                 }
315                 
316                 public override void Emit (EmitContext ec)
317                 {
318                         Emit (ec, false);
319                 }
320
321                 public override void EmitStatement (EmitContext ec)
322                 {
323                         Emit (ec, true);
324                 }
325         }
326
327         
328         //
329         // This class is used for compound assignments.  
330         //
331         class CompoundAssign : Assign {
332                 Binary.Operator op;
333                 public Expression original_source;
334                 
335                 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location l)
336                         : base (target, source, l)
337                 {
338                         original_source = source;
339                         this.op = op;
340                 }
341
342                 public Expression ResolveSource (EmitContext ec)
343                 {
344                         return original_source.Resolve (ec);
345                 }
346
347                 public override Expression DoResolve (EmitContext ec)
348                 {
349                         target = target.ResolveLValue (ec, source);
350                         if (target == null)
351                                 return null;
352
353                         original_source = original_source.Resolve (ec);
354                         if (original_source == null)
355                                 return null;
356
357                         //
358                         // Only now we can decouple the original source/target
359                         // into a tree, to guarantee that we do not have side
360                         // effects.
361                         //
362                         source = new Binary (op, target, original_source, l);
363                         return base.DoResolve (ec);
364                 }
365         }
366 }
367
368
369
370