Mercurial > projects > ddmd
annotate dmd/Expression.d @ 191:52188e7e3fb5
Fixed deprecated features, now compiles with DMD2.058
Also changed Array allocation policy:
Now doesn't reallocate but malloc's, followed by a memcpy (no free).
(this fixes a crash while compiling druntime. Same bug in dmd)
author | korDen@korDen-pc |
---|---|
date | Sun, 25 Mar 2012 03:11:12 +0400 |
parents | b0d41ff5e0df |
children |
rev | line source |
---|---|
0 | 1 module dmd.Expression; |
2 | |
114 | 3 import dmd.common; |
0 | 4 import dmd.Loc; |
5 import dmd.TOK; | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
6 import dmd.Parameter; |
12
832f71e6f96c
*Exp and *AssignExp arrayOp implementation added (might be a bit incomplete)
korDen
parents:
4
diff
changeset
|
7 import dmd.IdentifierExp; |
0 | 8 import dmd.Type; |
9 import dmd.WANT; | |
10 import dmd.Scope; | |
84
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
73
diff
changeset
|
11 import dmd.Array; |
0 | 12 import dmd.ArrayTypes; |
13 import dmd.OutBuffer; | |
14 import dmd.HdrGenState; | |
15 import dmd.MATCH; | |
16 import dmd.IntRange; | |
17 import dmd.Dsymbol; | |
18 import dmd.FuncDeclaration; | |
19 import dmd.InterState; | |
20 import dmd.InlineCostState; | |
21 import dmd.InlineDoState; | |
22 import dmd.InlineScanState; | |
23 import dmd.Identifier; | |
24 import dmd.IRState; | |
25 import dmd.DotIdExp; | |
26 import dmd.TypeExp; | |
27 import dmd.DYNCAST; | |
28 import dmd.TY; | |
29 import dmd.CallExp; | |
30 import dmd.VarExp; | |
31 import dmd.STC; | |
32 import dmd.TemplateInstance; | |
33 import dmd.CommaExp; | |
34 import dmd.NullExp; | |
35 import dmd.AddrExp; | |
55 | 36 import dmd.LINK; |
37 import dmd.FuncExp; | |
38 import dmd.ReturnStatement; | |
39 import dmd.Statement; | |
40 import dmd.FuncLiteralDeclaration; | |
41 import dmd.TypeFunction; | |
0 | 42 import dmd.ErrorExp; |
43 import dmd.TypeStruct; | |
44 import dmd.CastExp; | |
45 import dmd.Global; | |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
46 import dmd.GlobalExpressions; |
0 | 47 import dmd.Token; |
48 import dmd.TypeClass; | |
49 import dmd.PtrExp; | |
50 import dmd.TypeSArray; | |
51 import dmd.TypeReference; | |
52 import dmd.Util; | |
53 import dmd.Complex; | |
54 | |
55 import dmd.backend.elem; | |
141 | 56 import dmd.backend.Util; |
0 | 57 import dmd.backend.dt_t; |
58 | |
187
b0d41ff5e0df
Added expandability scheme outlined in http://www.dsource.org/forums/viewtopic.php?t=5659&sid=6f2150ff5b0bffcd47512a6a7608d218
Abscissa
parents:
179
diff
changeset
|
59 import dmd.DDMDExtensions; |
b0d41ff5e0df
Added expandability scheme outlined in http://www.dsource.org/forums/viewtopic.php?t=5659&sid=6f2150ff5b0bffcd47512a6a7608d218
Abscissa
parents:
179
diff
changeset
|
60 |
4 | 61 import core.memory; |
2 | 62 |
0 | 63 import std.stdio : writef; |
64 | |
65 import std.conv; | |
66 | |
67 /* Things like: | |
68 * int.size | |
69 * foo.size | |
70 * (foo).size | |
71 * cast(foo).size | |
72 */ | |
73 | |
74 Expression typeDotIdExp(Loc loc, Type type, Identifier ident) | |
75 { | |
76 return new DotIdExp(loc, new TypeExp(loc, type), ident); | |
77 } | |
78 | |
79 /***************************************** | |
80 * Determine if 'this' is available. | |
81 * If it is, return the FuncDeclaration that has it. | |
82 */ | |
83 | |
84 FuncDeclaration hasThis(Scope sc) | |
85 { | |
86 FuncDeclaration fd; | |
87 FuncDeclaration fdthis; | |
88 | |
89 //printf("hasThis()\n"); | |
90 fdthis = sc.parent.isFuncDeclaration(); | |
91 //printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis.toChars() : ""); | |
92 | |
93 // Go upwards until we find the enclosing member function | |
94 fd = fdthis; | |
95 while (1) | |
96 { | |
97 if (!fd) | |
98 { | |
99 goto Lno; | |
100 } | |
101 if (!fd.isNested()) | |
102 break; | |
103 | |
104 Dsymbol parent = fd.parent; | |
105 while (parent) | |
106 { | |
107 TemplateInstance ti = parent.isTemplateInstance(); | |
108 if (ti) | |
109 parent = ti.parent; | |
110 else | |
111 break; | |
112 } | |
113 | |
114 fd = fd.parent.isFuncDeclaration(); | |
115 } | |
116 | |
117 if (!fd.isThis()) | |
118 { | |
119 //printf("test '%s'\n", fd.toChars()); | |
120 goto Lno; | |
121 } | |
122 | |
123 assert(fd.vthis); | |
124 return fd; | |
125 | |
126 Lno: | |
127 return null; // don't have 'this' available | |
128 } | |
129 | |
130 /*************************************** | |
131 * Pull out any properties. | |
132 */ | |
133 Expression resolveProperties(Scope sc, Expression e) | |
134 { | |
135 //printf("resolveProperties(%s)\n", e.toChars()); | |
136 if (e.type) | |
137 { | |
138 Type t = e.type.toBasetype(); | |
139 | |
140 if (t.ty == TY.Tfunction || e.op == TOK.TOKoverloadset) | |
141 { | |
109 | 142 static if(false) |
143 { | |
144 if (t.ty == Tfunction && !(cast(TypeFunction)t).isproperty) | |
145 error(e.loc, "not a property %s\n", e.toChars()); | |
146 } | |
0 | 147 e = new CallExp(e.loc, e); |
148 e = e.semantic(sc); | |
149 } | |
150 | |
151 /* Look for e being a lazy parameter; rewrite as delegate call | |
152 */ | |
153 else if (e.op == TOK.TOKvar) | |
154 { VarExp ve = cast(VarExp)e; | |
155 | |
156 if (ve.var.storage_class & STC.STClazy) | |
157 { | |
158 e = new CallExp(e.loc, e); | |
159 e = e.semantic(sc); | |
160 } | |
161 } | |
162 | |
163 else if (e.op == TOK.TOKdotexp) | |
164 { | |
165 e.error("expression has no value"); | |
166 } | |
167 } | |
168 else if (e.op == TOK.TOKdottd) | |
169 { | |
170 e = new CallExp(e.loc, e); | |
171 e = e.semantic(sc); | |
172 } | |
173 | |
174 return e; | |
175 } | |
176 | |
95 | 177 void indent(int indent) |
178 { | |
179 foreach (i; 0 .. indent) | |
180 writef(" "); | |
181 } | |
182 | |
183 string type_print(Type type) | |
184 { | |
185 return type ? type.toChars() : "null"; | |
186 } | |
187 | |
178 | 188 import dmd.TObject; |
189 | |
190 class Expression : TObject | |
0 | 191 { |
187
b0d41ff5e0df
Added expandability scheme outlined in http://www.dsource.org/forums/viewtopic.php?t=5659&sid=6f2150ff5b0bffcd47512a6a7608d218
Abscissa
parents:
179
diff
changeset
|
192 mixin insertMemberExtension!(typeof(this)); |
b0d41ff5e0df
Added expandability scheme outlined in http://www.dsource.org/forums/viewtopic.php?t=5659&sid=6f2150ff5b0bffcd47512a6a7608d218
Abscissa
parents:
179
diff
changeset
|
193 |
0 | 194 Loc loc; // file location |
195 TOK op; // handy to minimize use of dynamic_cast | |
196 Type type; // !=null means that semantic() has been run | |
197 int size; // # of bytes in Expression so we can copy() it | |
198 | |
199 this(Loc loc, TOK op, int size) | |
200 { | |
178 | 201 register(); |
0 | 202 this.loc = loc; |
203 //writef("Expression.Expression(op = %d %s) this = %p\n", op, to!(string)(op), this); | |
204 this.op = op; | |
205 this.size = size; | |
206 type = null; | |
207 } | |
208 | |
45 | 209 bool equals(Object o) |
0 | 210 { |
211 return this is o; | |
212 } | |
213 | |
214 /********************************* | |
215 * Does *not* do a deep copy. | |
216 */ | |
178 | 217 Expression copy() |
0 | 218 { |
178 | 219 return cloneThis(this); |
0 | 220 } |
221 | |
222 Expression syntaxCopy() | |
223 { | |
224 //printf("Expression::syntaxCopy()\n"); | |
225 //dump(0); | |
226 return copy(); | |
227 } | |
228 | |
229 Expression semantic(Scope sc) | |
230 { | |
231 version (LOGSEMANTIC) { | |
232 printf("Expression.semantic() %s\n", toChars()); | |
233 } | |
234 if (type) | |
235 type = type.semantic(loc, sc); | |
236 else | |
237 type = Type.tvoid; | |
238 return this; | |
239 } | |
240 | |
241 Expression trySemantic(Scope sc) | |
242 { | |
243 uint errors = global.errors; | |
244 global.gag++; | |
245 Expression e = semantic(sc); | |
246 global.gag--; | |
247 if (errors != global.errors) | |
248 { | |
249 global.errors = errors; | |
250 e = null; | |
251 } | |
252 return e; | |
253 } | |
254 | |
255 DYNCAST dyncast() { return DYNCAST.DYNCAST_EXPRESSION; } // kludge for template.isExpression() | |
256 | |
257 void print() | |
258 { | |
259 assert(false); | |
260 } | |
261 | |
262 string toChars() | |
263 { | |
264 scope OutBuffer buf = new OutBuffer(); | |
265 HdrGenState hgs; | |
266 | |
267 toCBuffer(buf, &hgs); | |
268 return buf.toChars(); | |
269 } | |
270 | |
95 | 271 void dump(int i) |
0 | 272 { |
95 | 273 indent(i); |
274 writef("%p %s type=%s\n", this, Token.toChars(op), type_print(type)); | |
0 | 275 } |
276 | |
277 void error(T...)(string format, T t) | |
278 { | |
279 .error(loc, format, t); | |
280 } | |
281 | |
282 void warning(T...)(string formar, T t) | |
283 { | |
284 .warning(loc, format, t); | |
285 } | |
286 | |
287 void rvalue() | |
288 { | |
289 if (type && type.toBasetype().ty == TY.Tvoid) | |
290 { | |
291 error("expression %s is void and has no value", toChars()); | |
292 static if (false) { | |
293 dump(0); | |
294 halt(); | |
295 } | |
296 type = Type.terror; | |
297 } | |
298 } | |
299 | |
300 static Expression combine(Expression e1, Expression e2) | |
301 { | |
302 if (e1) | |
303 { | |
304 if (e2) | |
305 { | |
306 e1 = new CommaExp(e1.loc, e1, e2); | |
307 e1.type = e2.type; | |
308 } | |
309 } | |
310 else | |
311 { | |
312 e1 = e2; | |
313 } | |
314 | |
315 return e1; | |
316 } | |
317 | |
318 static Expressions arraySyntaxCopy(Expressions exps) | |
319 { | |
320 Expressions a = null; | |
321 | |
322 if (exps) | |
323 { | |
324 a = new Expressions(); | |
325 a.setDim(exps.dim); | |
84
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
73
diff
changeset
|
326 for (size_t i = 0; i < a.dim; i++) |
0 | 327 { |
84
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
73
diff
changeset
|
328 auto e = exps[i]; |
0 | 329 |
179 | 330 if (e) |
331 e = e.syntaxCopy(); | |
84
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
73
diff
changeset
|
332 a[i] = e; |
0 | 333 } |
334 } | |
335 return a; | |
336 } | |
337 | |
338 ulong toInteger() | |
339 { | |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
340 //printf("Expression %s\n", Token.toChars(op)); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
341 error("Integer constant expression expected instead of %s", toChars()); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
342 return 0; |
0 | 343 } |
344 | |
345 ulong toUInteger() | |
346 { | |
347 //printf("Expression %s\n", Token.toChars(op)); | |
348 return cast(ulong)toInteger(); | |
349 } | |
350 | |
351 real toReal() | |
352 { | |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
353 error("Floating point constant expression expected instead of %s", toChars()); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
354 return 0; |
0 | 355 } |
356 | |
357 real toImaginary() | |
358 { | |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
359 error("Floating point constant expression expected instead of %s", toChars()); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
360 return 0; |
0 | 361 } |
362 | |
363 Complex!(real) toComplex() | |
364 { | |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
365 error("Floating point constant expression expected instead of %s", toChars()); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
366 return Complex!real(0); |
0 | 367 } |
368 | |
369 void toCBuffer(OutBuffer buf, HdrGenState* hgs) | |
370 { | |
371 buf.writestring(Token.toChars(op)); | |
372 } | |
373 | |
374 void toMangleBuffer(OutBuffer buf) | |
375 { | |
55 | 376 error("expression %s is not a valid template value argument", toChars()); |
0 | 377 assert(false); |
55 | 378 version (DEBUG) { |
379 dump(0); | |
380 } | |
0 | 381 } |
382 | |
66 | 383 /*************************************** |
191
52188e7e3fb5
Fixed deprecated features, now compiles with DMD2.058
korDen@korDen-pc
parents:
187
diff
changeset
|
384 * Return true if expression is an lvalue. |
66 | 385 */ |
191
52188e7e3fb5
Fixed deprecated features, now compiles with DMD2.058
korDen@korDen-pc
parents:
187
diff
changeset
|
386 bool isLvalue() |
0 | 387 { |
191
52188e7e3fb5
Fixed deprecated features, now compiles with DMD2.058
korDen@korDen-pc
parents:
187
diff
changeset
|
388 return false; |
0 | 389 } |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
390 |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
391 /******************************* |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
392 * Give error if we're not an lvalue. |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
393 * If we can, convert expression to be an lvalue. |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
394 */ |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
395 Expression toLvalue(Scope sc, Expression e) |
0 | 396 { |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
397 if (!e) |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
398 e = this; |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
399 else if (!loc.filename) |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
400 loc = e.loc; |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
401 error("%s is not an lvalue", e.toChars()); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
402 return this; |
0 | 403 } |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
404 |
0 | 405 Expression modifiableLvalue(Scope sc, Expression e) |
406 { | |
407 //printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type.toChars()); | |
408 | |
409 // See if this expression is a modifiable lvalue (i.e. not const) | |
410 version (DMDV2) { | |
411 if (type && (!type.isMutable() || !type.isAssignable())) | |
412 error("%s is not mutable", e.toChars()); | |
413 } | |
414 return toLvalue(sc, e); | |
415 } | |
416 | |
417 /************************************** | |
418 * Do an implicit cast. | |
419 * Issue error if it can't be done. | |
420 */ | |
421 Expression implicitCastTo(Scope sc, Type t) | |
422 { | |
423 //printf("Expression.implicitCastTo(%s of type %s) => %s\n", toChars(), type.toChars(), t.toChars()); | |
424 | |
425 MATCH match = implicitConvTo(t); | |
426 if (match) | |
427 { | |
428 TY tyfrom = type.toBasetype().ty; | |
429 TY tyto = t.toBasetype().ty; | |
430 | |
431 version (DMDV1) { | |
432 if (global.params.warnings && | |
433 Type.impcnvWarn[tyfrom][tyto] && | |
434 op != TOKint64) | |
435 { | |
436 Expression e = optimize(WANT.WANTflags | WANT.WANTvalue); | |
437 | |
438 if (e.op == TOK.TOKint64) | |
439 return e.implicitCastTo(sc, t); | |
440 if (tyfrom == Tint32 && (op == TOKadd || op == TOKmin || op == TOKand || op == TOKor || op == TOKxor)) | |
441 { | |
442 /* This is really only a semi-kludge fix, | |
443 * we really should look at the operands of op | |
444 * and see if they are narrower types. | |
445 * For example, b=b|b and b=b|7 and s=b+b should be allowed, | |
446 * but b=b|i should be an error. | |
447 */ | |
448 ; | |
449 } | |
450 else | |
451 { | |
452 warning("implicit conversion of expression (%s) of type %s to %s can cause loss of data", toChars(), type.toChars(), t.toChars()); | |
453 } | |
454 } | |
455 } | |
456 version (DMDV2) { | |
457 if (match == MATCH.MATCHconst && t == type.constOf()) | |
458 { | |
459 Expression e = copy(); | |
460 e.type = t; | |
461 return e; | |
462 } | |
463 } | |
464 return castTo(sc, t); | |
465 } | |
466 | |
467 Expression e = optimize(WANT.WANTflags | WANT.WANTvalue); | |
468 if (e != this) | |
469 return e.implicitCastTo(sc, t); | |
470 | |
471 static if (false) { | |
472 printf("ty = %d\n", type.ty); | |
473 print(); | |
474 type.print(); | |
475 printf("to:\n"); | |
476 t.print(); | |
477 printf("%p %p type: %s to: %s\n", type.deco, t.deco, type.deco, t.deco); | |
478 //printf("%p %p %p\n", type.nextOf().arrayOf(), type, t); | |
479 fflush(stdout); | |
480 } | |
481 if (!t.deco) { | |
482 /* Can happen with: | |
483 * enum E { One } | |
484 * class A | |
485 * { static void fork(EDG dg) { dg(E.One); } | |
486 * alias void delegate(E) EDG; | |
487 * } | |
488 * Should eventually make it work. | |
489 */ | |
490 error("forward reference to type %s", t.toChars()); | |
491 } else if (t.reliesOnTident()) { | |
492 error("forward reference to type %s", t.reliesOnTident().toChars()); | |
493 } | |
494 | |
495 error("cannot implicitly convert expression (%s) of type %s to %s", toChars(), type.toChars(), t.toChars()); | |
496 return castTo(sc, t); | |
497 } | |
498 | |
499 /******************************************* | |
500 * Return !=0 if we can implicitly convert this to type t. | |
501 * Don't do the actual cast. | |
502 */ | |
503 MATCH implicitConvTo(Type t) | |
504 { | |
505 static if (false) { | |
506 printf("Expression.implicitConvTo(this=%s, type=%s, t=%s)\n", | |
507 toChars(), type.toChars(), t.toChars()); | |
508 } | |
509 //static int nest; if (++nest == 10) halt(); | |
510 if (!type) | |
511 { | |
512 error("%s is not an expression", toChars()); | |
513 type = Type.terror; | |
514 } | |
515 Expression e = optimize(WANT.WANTvalue | WANT.WANTflags); | |
516 if (e.type == t) | |
517 return MATCH.MATCHexact; | |
518 if (e != this) | |
519 { | |
520 //printf("\toptimized to %s of type %s\n", e.toChars(), e.type.toChars()); | |
521 return e.implicitConvTo(t); | |
522 } | |
523 MATCH match = type.implicitConvTo(t); | |
524 if (match != MATCH.MATCHnomatch) | |
525 return match; | |
526 | |
527 /* See if we can do integral narrowing conversions | |
528 */ | |
529 if (type.isintegral() && t.isintegral() && | |
530 type.isTypeBasic() && t.isTypeBasic()) | |
531 { | |
532 IntRange ir = getIntRange(); | |
533 if (ir.imax <= t.sizemask()) | |
534 return MATCH.MATCHconvert; | |
535 } | |
536 | |
537 static if (false) { | |
538 Type tb = t.toBasetype(); | |
539 if (tb.ty == Tdelegate) | |
540 { | |
541 TypeDelegate td = cast(TypeDelegate)tb; | |
542 TypeFunction tf = cast(TypeFunction)td.nextOf(); | |
543 | |
544 if (!tf.varargs && !(tf.arguments && tf.arguments.dim)) | |
545 { | |
546 match = type.implicitConvTo(tf.nextOf()); | |
547 if (match) | |
548 return match; | |
549 if (tf.nextOf().toBasetype().ty == Tvoid) | |
550 return MATCH.MATCHconvert; | |
551 } | |
552 } | |
553 } | |
554 return MATCH.MATCHnomatch; | |
555 } | |
556 | |
557 IntRange getIntRange() | |
558 { | |
53 | 559 IntRange ir; |
560 ir.imin = 0; | |
73 | 561 if (type.isintegral()) |
562 ir.imax = type.sizemask(); | |
563 else | |
564 ir.imax = 0xFFFFFFFFFFFFFFFFUL; // assume the worst | |
565 | |
53 | 566 return ir; |
0 | 567 } |
568 | |
569 /************************************** | |
570 * Do an explicit cast. | |
571 */ | |
572 Expression castTo(Scope sc, Type t) | |
573 { | |
574 //printf("Expression.castTo(this=%s, t=%s)\n", toChars(), t.toChars()); | |
575 static if (false) { | |
576 writef("Expression.castTo(this=%s, type=%s, t=%s)\n", | |
577 toChars(), type.toChars(), t.toChars()); | |
578 } | |
579 if (type is t) | |
580 return this; | |
581 Expression e = this; | |
582 Type tb = t.toBasetype(); | |
583 Type typeb = type.toBasetype(); | |
584 if (tb != typeb) | |
585 { | |
586 // Do (type *) cast of (type [dim]) | |
587 if (tb.ty == TY.Tpointer && typeb.ty == TY.Tsarray | |
588 ) | |
589 { | |
590 //printf("Converting [dim] to *\n"); | |
591 | |
592 if (typeb.size(loc) == 0) | |
593 e = new NullExp(loc); | |
594 else | |
595 e = new AddrExp(loc, e); | |
596 } | |
597 else { | |
598 static if (false) { | |
599 if (tb.ty == Tdelegate && type.ty != Tdelegate) | |
600 { | |
601 TypeDelegate td = cast(TypeDelegate)tb; | |
602 TypeFunction tf = cast(TypeFunction)td.nextOf(); | |
603 return toDelegate(sc, tf.nextOf()); | |
604 } | |
605 } | |
606 if (typeb.ty == TY.Tstruct) | |
607 { | |
608 TypeStruct ts = cast(TypeStruct)typeb; | |
609 if (!(tb.ty == TY.Tstruct && ts.sym == (cast(TypeStruct)tb).sym) && | |
610 ts.sym.aliasthis) | |
611 { /* Forward the cast to our alias this member, rewrite to: | |
612 * cast(to)e1.aliasthis | |
613 */ | |
614 Expression e1 = new DotIdExp(loc, this, ts.sym.aliasthis.ident); | |
615 Expression e2 = new CastExp(loc, e1, tb); | |
616 e2 = e2.semantic(sc); | |
617 return e2; | |
618 } | |
619 } | |
620 else if (typeb.ty == TY.Tclass) | |
621 { | |
622 TypeClass ts = cast(TypeClass)typeb; | |
623 if (tb.ty != TY.Tclass && ts.sym.aliasthis) | |
624 { /* Forward the cast to our alias this member, rewrite to: | |
625 * cast(to)e1.aliasthis | |
626 */ | |
627 Expression e1 = new DotIdExp(loc, this, ts.sym.aliasthis.ident); | |
628 Expression e2 = new CastExp(loc, e1, tb); | |
629 e2 = e2.semantic(sc); | |
630 return e2; | |
631 } | |
632 } | |
633 e = new CastExp(loc, e, tb); | |
634 } | |
635 } | |
636 else | |
637 { | |
638 e = e.copy(); // because of COW for assignment to e.type | |
639 } | |
640 | |
641 assert(e != this); | |
642 e.type = t; | |
643 //printf("Returning: %s\n", e.toChars()); | |
644 return e; | |
645 } | |
646 | |
647 /************************************ | |
648 * Detect cases where pointers to the stack can 'escape' the | |
649 * lifetime of the stack frame. | |
650 */ | |
651 void checkEscape() | |
652 { | |
653 } | |
654 | |
135 | 655 void checkEscapeRef() |
656 { | |
657 } | |
658 | |
0 | 659 void checkScalar() |
660 { | |
661 if (!type.isscalar()) | |
662 error("'%s' is not a scalar, it is a %s", toChars(), type.toChars()); | |
663 | |
664 rvalue(); | |
665 } | |
666 | |
667 void checkNoBool() | |
668 { | |
669 if (type.toBasetype().ty == TY.Tbool) | |
670 error("operation not allowed on bool '%s'", toChars()); | |
671 } | |
672 | |
673 Expression checkIntegral() | |
674 { | |
675 if (!type.isintegral()) | |
676 { | |
677 error("'%s' is not of integral type, it is a %s", toChars(), type.toChars()); | |
678 return new ErrorExp(); | |
679 } | |
680 | |
681 rvalue(); | |
682 return this; | |
683 } | |
684 | |
685 Expression checkArithmetic() | |
686 { | |
687 if (!type.isintegral() && !type.isfloating()) | |
688 { | |
689 error("'%s' is not of arithmetic type, it is a %s", toChars(), type.toChars()); | |
690 return new ErrorExp(); | |
691 } | |
692 | |
693 rvalue(); | |
694 return this; | |
695 } | |
696 | |
697 void checkDeprecated(Scope sc, Dsymbol s) | |
698 { | |
699 s.checkDeprecated(loc, sc); | |
700 } | |
701 | |
702 void checkPurity(Scope sc, FuncDeclaration f) | |
703 { | |
704 static if (true) { | |
705 if (sc.func) | |
706 { | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
707 /* Given: |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
708 * void f() |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
709 * { pure void g() |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
710 * { |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
711 * void h() |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
712 * { |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
713 * void i() { } |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
714 * } |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
715 * } |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
716 * } |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
717 * g() can call h() but not f() |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
718 * i() can call h() and g() but not f() |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
719 */ |
0 | 720 FuncDeclaration outerfunc = sc.func; |
721 while (outerfunc.toParent2() && outerfunc.toParent2().isFuncDeclaration()) | |
722 { | |
723 outerfunc = outerfunc.toParent2().isFuncDeclaration(); | |
724 } | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
725 if (outerfunc.isPure() && !sc.intypeof && (!f.isNested() && !f.isPure())) |
0 | 726 error("pure function '%s' cannot call impure function '%s'\n", |
727 sc.func.toChars(), f.toChars()); | |
728 } | |
729 } else { | |
730 if (sc.func && sc.func.isPure() && !sc.intypeof && !f.isPure()) | |
731 error("pure function '%s' cannot call impure function '%s'\n", | |
732 sc.func.toChars(), .toChars()); | |
733 } | |
734 } | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
735 |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
736 void checkSafety(Scope sc, FuncDeclaration f) |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
737 { |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
738 if (sc.func && sc.func.isSafe() && !sc.intypeof && |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
739 !f.isSafe() && !f.isTrusted()) |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
740 error("safe function '%s' cannot call system function '%s'\n", |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
741 sc.func.toChars(), f.toChars()); |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
742 } |
0 | 743 |
744 /***************************** | |
745 * Check that expression can be tested for true or false. | |
746 */ | |
747 Expression checkToBoolean() | |
748 { | |
749 // Default is 'yes' - do nothing | |
750 | |
751 debug { | |
752 if (!type) | |
753 dump(0); | |
754 } | |
755 | |
756 if (!type.checkBoolean()) | |
757 { | |
758 error("expression %s of type %s does not have a boolean value", toChars(), type.toChars()); | |
759 } | |
760 | |
761 return this; | |
762 } | |
763 | |
764 Expression checkToPointer() | |
765 { | |
109 | 766 //writef("Expression::checkToPointer()\n"); |
767 Expression e = this; | |
0 | 768 |
109 | 769 version(SARRAYVALUE) {} else |
770 { | |
0 | 771 // If C static array, convert to pointer |
109 | 772 Type tb = type.toBasetype(); |
0 | 773 if (tb.ty == Tsarray) |
774 { | |
775 TypeSArray ts = cast(TypeSArray)tb; | |
776 if (ts.size(loc) == 0) | |
777 e = new NullExp(loc); | |
778 else | |
779 e = new AddrExp(loc, this); | |
780 e.type = ts.next.pointerTo(); | |
781 } | |
109 | 782 } |
0 | 783 return e; |
784 } | |
785 | |
786 Expression addressOf(Scope sc) | |
787 { | |
788 //printf("Expression::addressOf()\n"); | |
789 Expression e = toLvalue(sc, null); | |
790 e = new AddrExp(loc, e); | |
791 e.type = type.pointerTo(); | |
792 return e; | |
793 } | |
794 | |
795 /****************************** | |
796 * If this is a reference, dereference it. | |
797 */ | |
798 Expression deref() | |
799 { | |
800 //printf("Expression::deref()\n"); | |
73 | 801 // type could be null if forward referencing an 'auto' variable |
802 if (type && type.ty == Treference) | |
803 { | |
0 | 804 Expression e = new PtrExp(loc, this); |
805 e.type = (cast(TypeReference)type).next; | |
806 return e; | |
807 } | |
808 return this; | |
809 } | |
810 | |
811 /*********************************** | |
812 * Do integral promotions (convertchk). | |
813 * Don't convert <array of> to <pointer to> | |
814 */ | |
815 Expression integralPromotions(Scope sc) | |
816 { | |
817 Expression e = this; | |
818 | |
819 //printf("integralPromotions %s %s\n", e.toChars(), e.type.toChars()); | |
820 switch (type.toBasetype().ty) | |
821 { | |
822 case TY.Tvoid: | |
823 error("void has no value"); | |
824 break; | |
825 | |
826 case TY.Tint8: | |
827 case TY.Tuns8: | |
828 case TY.Tint16: | |
829 case TY.Tuns16: | |
830 case TY.Tbit: | |
831 case TY.Tbool: | |
832 case TY.Tchar: | |
833 case TY.Twchar: | |
834 e = e.castTo(sc, Type.tint32); | |
835 break; | |
836 | |
837 case TY.Tdchar: | |
838 e = e.castTo(sc, Type.tuns32); | |
839 break; | |
840 default: | |
841 break; /// | |
842 } | |
843 return e; | |
844 } | |
845 | |
55 | 846 /******************************************** |
847 * Convert from expression to delegate that returns the expression, | |
848 * i.e. convert: | |
849 * expr | |
850 * to: | |
851 * t delegate() { return expr; } | |
852 */ | |
0 | 853 Expression toDelegate(Scope sc, Type t) |
854 { | |
55 | 855 //printf("Expression.toDelegate(t = %s) %s\n", t.toChars(), toChars()); |
856 TypeFunction tf = new TypeFunction(null, t, 0, LINKd); | |
857 FuncLiteralDeclaration fld = new FuncLiteralDeclaration(loc, loc, tf, TOKdelegate, null); | |
858 Expression e; | |
859 static if (true) { | |
860 sc = sc.push(); | |
861 sc.parent = fld; // set current function to be the delegate | |
862 e = this; | |
863 e.scanForNestedRef(sc); | |
864 sc = sc.pop(); | |
865 } else { | |
866 e = this.syntaxCopy(); | |
867 } | |
868 Statement s = new ReturnStatement(loc, e); | |
869 fld.fbody = s; | |
870 e = new FuncExp(loc, fld); | |
871 e = e.semantic(sc); | |
872 return e; | |
0 | 873 } |
874 | |
875 void scanForNestedRef(Scope sc) | |
876 { | |
55 | 877 //printf("Expression.scanForNestedRef(%s)\n", toChars()); |
0 | 878 } |
879 | |
880 Expression optimize(int result) | |
881 { | |
882 //printf("Expression.optimize(result = x%x) %s\n", result, toChars()); | |
883 return this; | |
884 } | |
885 | |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
886 Expression interpret(InterState istate) |
0 | 887 { |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
888 version(LOG) |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
889 { |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
890 writef("Expression::interpret() %s\n", toChars()); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
891 writef("type = %s\n", type.toChars()); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
892 dump(0); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
893 } |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
894 error("Cannot interpret %s at compile time", toChars()); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
84
diff
changeset
|
895 return EXP_CANT_INTERPRET; |
0 | 896 } |
897 | |
898 int isConst() | |
899 { | |
900 //printf("Expression::isConst(): %s\n", toChars()); | |
901 return 0; | |
902 } | |
903 | |
904 /******************************** | |
905 * Does this expression statically evaluate to a boolean TRUE or FALSE? | |
906 */ | |
907 bool isBool(bool result) | |
908 { | |
909 return false; | |
910 } | |
911 | |
141 | 912 /******************************** |
913 * Does this expression result in either a 1 or a 0? | |
914 */ | |
191
52188e7e3fb5
Fixed deprecated features, now compiles with DMD2.058
korDen@korDen-pc
parents:
187
diff
changeset
|
915 bool isBit() |
0 | 916 { |
141 | 917 return false; |
0 | 918 } |
919 | |
920 /******************************** | |
921 * Check for expressions that have no use. | |
922 * Input: | |
923 * flag 0 not going to use the result, so issue error message if no | |
924 * side effects | |
925 * 1 the result of the expression is used, but still check | |
926 * for useless subexpressions | |
927 * 2 do not issue error messages, just return !=0 if expression | |
928 * has side effects | |
929 */ | |
930 bool checkSideEffect(int flag) | |
931 { | |
932 if (flag == 0) | |
933 { | |
73 | 934 if (op == TOKerror) |
935 { | |
936 // Error should have already been printed | |
937 } | |
938 else if (op == TOKimport) | |
0 | 939 error("%s has no effect", toChars()); |
940 else | |
941 error("%s has no effect in expression (%s)", | |
942 | |
943 Token.toChars(op), toChars()); | |
944 } | |
945 | |
946 return false; | |
947 } | |
948 | |
949 bool canThrow() | |
950 { | |
951 version (DMDV2) { | |
952 return false; | |
953 } else { | |
954 return true; | |
955 } | |
956 } | |
957 | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
958 /**************************************** |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
959 * Resolve __LINE__ and __FILE__ to loc. |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
960 */ |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
961 Expression resolveLoc(Loc loc, Scope sc) |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
962 { |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
963 return this; |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
964 } |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
965 |
0 | 966 int inlineCost(InlineCostState* ics) |
967 { | |
968 return 1; | |
969 } | |
970 | |
971 Expression doInline(InlineDoState ids) | |
972 { | |
973 //printf("Expression.doInline(%s): %s\n", Token.toChars(op), toChars()); | |
974 return copy(); | |
975 } | |
976 | |
977 Expression inlineScan(InlineScanState* iss) | |
978 { | |
979 return this; | |
980 } | |
981 | |
982 /*********************************** | |
983 * Determine if operands of binary op can be reversed | |
984 * to fit operator overload. | |
985 */ | |
986 | |
987 // For operator overloading | |
988 bool isCommutative() | |
989 { | |
990 return false; // default is no reverse | |
991 } | |
992 | |
993 /*********************************** | |
994 * Get Identifier for operator overload. | |
995 */ | |
996 Identifier opId() | |
997 { | |
998 assert(false); | |
999 } | |
1000 | |
1001 /*********************************** | |
1002 * Get Identifier for reverse operator overload, | |
1003 * null if not supported for this operator. | |
1004 */ | |
1005 Identifier opId_r() | |
1006 { | |
1007 return null; | |
1008 } | |
1009 | |
1010 // For array ops | |
12
832f71e6f96c
*Exp and *AssignExp arrayOp implementation added (might be a bit incomplete)
korDen
parents:
4
diff
changeset
|
1011 /****************************************** |
832f71e6f96c
*Exp and *AssignExp arrayOp implementation added (might be a bit incomplete)
korDen
parents:
4
diff
changeset
|
1012 * Construct the identifier for the array operation function, |
832f71e6f96c
*Exp and *AssignExp arrayOp implementation added (might be a bit incomplete)
korDen
parents:
4
diff
changeset
|
1013 * and build the argument list to pass to it. |
832f71e6f96c
*Exp and *AssignExp arrayOp implementation added (might be a bit incomplete)
korDen
parents:
4
diff
changeset
|
1014 */ |
0 | 1015 void buildArrayIdent(OutBuffer buf, Expressions arguments) |
1016 { | |
12
832f71e6f96c
*Exp and *AssignExp arrayOp implementation added (might be a bit incomplete)
korDen
parents:
4
diff
changeset
|
1017 buf.writestring("Exp"); |
84
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
73
diff
changeset
|
1018 arguments.shift(this); |
0 | 1019 } |
1020 | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
1021 Expression buildArrayLoop(Parameters fparams) |
0 | 1022 { |
12
832f71e6f96c
*Exp and *AssignExp arrayOp implementation added (might be a bit incomplete)
korDen
parents:
4
diff
changeset
|
1023 Identifier id = Identifier.generateId("c", fparams.dim); |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
1024 auto param = new Parameter(STC.STCundefined, type, id, null); |
126
1765f3ef917d
ClassDeclarations, Arguments -> Vector
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
114
diff
changeset
|
1025 fparams.shift(param); |
12
832f71e6f96c
*Exp and *AssignExp arrayOp implementation added (might be a bit incomplete)
korDen
parents:
4
diff
changeset
|
1026 Expression e = new IdentifierExp(Loc(0), id); |
832f71e6f96c
*Exp and *AssignExp arrayOp implementation added (might be a bit incomplete)
korDen
parents:
4
diff
changeset
|
1027 return e; |
0 | 1028 } |
95 | 1029 |
1030 /*********************************************** | |
1031 * Test if operand is a valid array op operand. | |
1032 */ | |
1033 int isArrayOperand() | |
1034 { | |
1035 //writef("Expression::isArrayOperand() %s\n", toChars()); | |
1036 if (op == TOKslice) | |
1037 return 1; | |
1038 if (type.toBasetype().ty == TY.Tarray) | |
1039 { | |
1040 switch (op) | |
1041 { | |
1042 case TOKadd: | |
1043 case TOKmin: | |
1044 case TOKmul: | |
1045 case TOKdiv: | |
1046 case TOKmod: | |
1047 case TOKxor: | |
1048 case TOKand: | |
1049 case TOKor: | |
1050 case TOKneg: | |
1051 case TOKtilde: | |
1052 return 1; | |
1053 | |
1054 default: | |
1055 break; | |
1056 } | |
1057 } | |
1058 return 0; | |
1059 } | |
1060 | |
0 | 1061 // Back end |
1062 elem* toElem(IRState* irs) | |
1063 { | |
141 | 1064 print(); |
0 | 1065 assert(false); |
141 | 1066 return null; |
0 | 1067 } |
1068 | |
1069 dt_t** toDt(dt_t** pdt) | |
1070 { | |
141 | 1071 debug |
1072 { | |
1073 writef("Expression::toDt() %d\n", op); | |
1074 dump(0); | |
1075 } | |
1076 error("non-constant expression %s", toChars()); | |
1077 pdt = dtnzeros(pdt, 1); | |
1078 return pdt; | |
0 | 1079 } |
16
5c9b78899f5d
Implemented methods for Tuples, fixed some linking issues.
Robert Clipsham <robert@octarineparrot.com>
parents:
4
diff
changeset
|
1080 } |
84
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
73
diff
changeset
|
1081 |
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
73
diff
changeset
|
1082 alias Vector!Expression Expressions; |