Mercurial > projects > ddmd
annotate dmd/TypeClass.d @ 84:be2ab491772e
Expressions -> Vector!Expression
author | Eldar Insafutdinov <e.insafutdinov@gmail.com> |
---|---|
date | Mon, 30 Aug 2010 16:12:19 +0100 |
parents | 43073c7c7769 |
children | e28b18c23469 |
rev | line source |
---|---|
0 | 1 module dmd.TypeClass; |
2 | |
3 import dmd.Type; | |
4 import dmd.ClassDeclaration; | |
64 | 5 import dmd.TypeInstance; |
0 | 6 import dmd.Loc; |
7 import dmd.Dsymbol; | |
8 import dmd.Scope; | |
9 import dmd.OutBuffer; | |
10 import dmd.HdrGenState; | |
11 import dmd.Expression; | |
12 import dmd.Identifier; | |
13 import dmd.MATCH; | |
64 | 14 import dmd.DYNCAST; |
0 | 15 import dmd.CppMangleState; |
16 import dmd.ArrayTypes; | |
17 import dmd.TypeInfoDeclaration; | |
18 import dmd.TY; | |
19 import dmd.MOD; | |
20 import dmd.Global; | |
21 import dmd.TypePointer; | |
22 import dmd.Declaration; | |
23 import dmd.VarDeclaration; | |
24 import dmd.TOK; | |
25 import dmd.DotExp; | |
26 import dmd.Id; | |
27 import dmd.ScopeExp; | |
28 import dmd.DotVarExp; | |
29 import dmd.VarExp; | |
30 import dmd.PtrExp; | |
31 import dmd.AddExp; | |
32 import dmd.IntegerExp; | |
33 import dmd.DotIdExp; | |
34 import dmd.EnumMember; | |
35 import dmd.TemplateMixin; | |
36 import dmd.TemplateDeclaration; | |
37 import dmd.TemplateInstance; | |
38 import dmd.OverloadSet; | |
39 import dmd.DotTypeExp; | |
40 import dmd.TupleExp; | |
41 import dmd.ClassInfoDeclaration; | |
42 import dmd.TypeInfoInterfaceDeclaration; | |
43 import dmd.TypeInfoClassDeclaration; | |
44 import dmd.Util; | |
45 import dmd.NullExp; | |
46 import dmd.TypeExp; | |
47 import dmd.DotTemplateExp; | |
48 import dmd.ErrorExp; | |
49 import dmd.ThisExp; | |
50 import dmd.CommaExp; | |
51 | |
52 import dmd.expression.Util; | |
53 import dmd.backend.Symbol; | |
54 import dmd.backend.TYPE; | |
55 import dmd.backend.Util; | |
56 import dmd.backend.SC; | |
57 import dmd.backend.STR; | |
58 import dmd.backend.TYM; | |
59 import dmd.backend.LIST; | |
60 import dmd.backend.Classsym; | |
61 | |
62 import std.string : toStringz; | |
63 | |
64 class TypeClass : Type | |
65 { | |
66 ClassDeclaration sym; | |
67 | |
68 this(ClassDeclaration sym) | |
69 { | |
70 super(TY.Tclass); | |
71 this.sym = sym; | |
72 } | |
73 | |
74 version (DumbClone) { | |
75 } else { | |
76 Type clone() | |
77 { | |
78 assert(false); | |
79 } | |
80 } | |
72 | 81 override ulong size(Loc loc) |
0 | 82 { |
83 return PTRSIZE; | |
84 } | |
85 | |
72 | 86 override string toChars() |
0 | 87 { |
88 if (mod) | |
89 return Type.toChars(); | |
90 return sym.toPrettyChars(); | |
91 } | |
92 | |
72 | 93 override Type syntaxCopy() |
0 | 94 { |
95 assert(false); | |
96 } | |
97 | |
72 | 98 override Type semantic(Loc loc, Scope sc) |
0 | 99 { |
100 //printf("TypeClass.semantic(%s)\n", sym.toChars()); | |
101 if (deco) | |
102 return this; | |
103 //printf("\t%s\n", merge().deco); | |
104 return merge(); | |
105 } | |
106 | |
72 | 107 override Dsymbol toDsymbol(Scope sc) |
0 | 108 { |
109 return sym; | |
110 } | |
111 | |
72 | 112 override void toDecoBuffer(OutBuffer buf, int flag) |
0 | 113 { |
114 string name = sym.mangle(); | |
115 //printf("TypeClass.toDecoBuffer('%s' flag=%d mod=%x) = '%s'\n", toChars(), flag, mod, name); | |
116 Type.toDecoBuffer(buf, flag); | |
117 buf.printf("%s", name); | |
118 } | |
119 | |
72 | 120 override void toCBuffer2(OutBuffer buf, HdrGenState* hgs, MOD mod) |
0 | 121 { |
122 if (mod != this.mod) | |
123 { | |
124 toCBuffer3(buf, hgs, mod); | |
125 return; | |
126 } | |
127 buf.writestring(sym.toChars()); | |
128 } | |
129 | |
72 | 130 override Expression dotExp(Scope sc, Expression e, Identifier ident) |
0 | 131 { |
132 uint offset; | |
133 | |
134 Expression b; | |
135 VarDeclaration v; | |
136 Dsymbol s; | |
137 | |
138 version (LOGDOTEXP) { | |
139 printf("TypeClass.dotExp(e='%s', ident='%s')\n", e.toChars(), ident.toChars()); | |
140 } | |
141 | |
142 if (e.op == TOK.TOKdotexp) | |
143 { | |
144 DotExp de = cast(DotExp)e; | |
145 | |
146 if (de.e1.op == TOK.TOKimport) | |
147 { | |
148 ScopeExp se = cast(ScopeExp)de.e1; | |
149 | |
150 s = se.sds.search(e.loc, ident, 0); | |
151 e = de.e1; | |
152 goto L1; | |
153 } | |
154 } | |
155 | |
156 if (ident is Id.tupleof_) | |
157 { | |
158 /* Create a TupleExp | |
159 */ | |
160 e = e.semantic(sc); // do this before turning on noaccesscheck | |
161 Expressions exps = new Expressions; | |
162 exps.reserve(sym.fields.dim); | |
163 for (size_t i = 0; i < sym.fields.dim; i++) | |
164 { | |
79 | 165 VarDeclaration v2 = cast(VarDeclaration)sym.fields[i]; |
0 | 166 Expression fe = new DotVarExp(e.loc, e, v2); |
84
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
79
diff
changeset
|
167 exps.push(fe); |
0 | 168 } |
169 e = new TupleExp(e.loc, exps); | |
170 sc = sc.push(); | |
171 sc.noaccesscheck = 1; | |
172 e = e.semantic(sc); | |
173 sc.pop(); | |
174 return e; | |
175 } | |
176 | |
177 s = sym.search(e.loc, ident, 0); | |
178 L1: | |
179 if (!s) | |
180 { | |
181 // See if it's a base class | |
182 ClassDeclaration cbase; | |
183 for (cbase = sym.baseClass; cbase; cbase = cbase.baseClass) | |
184 { | |
185 if (cbase.ident.equals(ident)) | |
186 { | |
187 e = new DotTypeExp(Loc(0), e, cbase); | |
188 return e; | |
189 } | |
190 } | |
191 | |
192 if (ident is Id.classinfo_) | |
193 { | |
194 assert(ClassDeclaration.classinfo); | |
195 Type t = ClassDeclaration.classinfo.type; | |
196 if (e.op == TOK.TOKtype || e.op == TOK.TOKdottype) | |
197 { | |
198 /* For type.classinfo, we know the classinfo | |
199 * at compile time. | |
200 */ | |
201 if (!sym.vclassinfo) | |
202 sym.vclassinfo = new ClassInfoDeclaration(sym); | |
203 | |
204 e = new VarExp(e.loc, sym.vclassinfo); | |
205 e = e.addressOf(sc); | |
206 e.type = t; // do this so we don't get redundant dereference | |
207 } | |
208 else | |
209 { | |
210 /* For class objects, the classinfo reference is the first | |
211 * entry in the vtbl[] | |
212 */ | |
213 e = new PtrExp(e.loc, e); | |
214 e.type = t.pointerTo(); | |
215 if (sym.isInterfaceDeclaration()) | |
216 { | |
217 if (sym.isCPPinterface()) | |
218 { | |
219 /* C++ interface vtbl[]s are different in that the | |
220 * first entry is always pointer to the first virtual | |
221 * function, not classinfo. | |
222 * We can't get a .classinfo for it. | |
223 */ | |
224 error(e.loc, "no .classinfo for C++ interface objects"); | |
225 } | |
226 /* For an interface, the first entry in the vtbl[] | |
227 * is actually a pointer to an instance of struct Interface. | |
228 * The first member of Interface is the .classinfo, | |
229 * so add an extra pointer indirection. | |
230 */ | |
231 e.type = e.type.pointerTo(); | |
232 e = new PtrExp(e.loc, e); | |
233 e.type = t.pointerTo(); | |
234 } | |
235 e = new PtrExp(e.loc, e, t); | |
236 } | |
237 return e; | |
238 } | |
239 | |
240 if (ident is Id.__vptr) | |
241 { | |
242 /* The pointer to the vtbl[] | |
243 * *cast(invariant(void*)**)e | |
244 */ | |
245 e = e.castTo(sc, tvoidptr.invariantOf().pointerTo().pointerTo()); | |
246 e = new PtrExp(e.loc, e); | |
247 e = e.semantic(sc); | |
248 return e; | |
249 } | |
250 | |
251 if (ident is Id.__monitor) | |
252 { /* The handle to the monitor (call it a void*) | |
253 * *(cast(void**)e + 1) | |
254 */ | |
255 e = e.castTo(sc, tvoidptr.pointerTo()); | |
256 e = new AddExp(e.loc, e, new IntegerExp(1)); | |
257 e = new PtrExp(e.loc, e); | |
258 e = e.semantic(sc); | |
259 return e; | |
260 } | |
261 | |
262 if (ident is Id.typeinfo_) | |
263 { | |
264 if (!global.params.useDeprecated) | |
265 error(e.loc, ".typeinfo deprecated, use typeid(type)"); | |
266 | |
267 return getTypeInfo(sc); | |
268 } | |
269 if (ident is Id.outer && sym.vthis) | |
270 { | |
271 s = sym.vthis; | |
272 } | |
273 else | |
274 { | |
275 if (ident !is Id.__sizeof && | |
276 ident !is Id.alignof_ && | |
277 ident !is Id.init_ && | |
278 ident !is Id.mangleof_ && | |
279 ident !is Id.stringof_ && | |
280 ident !is Id.offsetof) | |
281 { | |
282 /* See if we should forward to the alias this. | |
283 */ | |
284 if (sym.aliasthis) | |
285 { | |
286 /* Rewrite e.ident as: | |
287 * e.aliasthis.ident | |
288 */ | |
289 e = new DotIdExp(e.loc, e, sym.aliasthis.ident); | |
290 e = new DotIdExp(e.loc, e, ident); | |
291 return e.semantic(sc); | |
292 } | |
293 | |
294 /* Look for overloaded opDot() to see if we should forward request | |
295 * to it. | |
296 */ | |
297 Dsymbol fd = search_function(sym, Id.opDot); | |
298 if (fd) | |
299 { | |
300 /* Rewrite e.ident as: | |
301 * e.opId().ident | |
302 */ | |
303 e = build_overload(e.loc, sc, e, null, fd.ident); | |
304 e = new DotIdExp(e.loc, e, ident); | |
305 return e.semantic(sc); | |
306 } | |
307 } | |
308 | |
309 return Type.dotExp(sc, e, ident); | |
310 } | |
311 } | |
312 | |
313 if (!s.isFuncDeclaration()) // because of overloading | |
314 s.checkDeprecated(e.loc, sc); | |
315 | |
316 s = s.toAlias(); | |
317 v = s.isVarDeclaration(); | |
318 | |
319 if (v && !v.isDataseg()) | |
320 { | |
321 Expression ei = v.getConstInitializer(); | |
322 | |
323 if (ei) | |
324 { | |
325 e = ei.copy(); // need to copy it if it's a StringExp | |
326 e = e.semantic(sc); | |
327 return e; | |
328 } | |
329 } | |
330 | |
331 if (s.getType()) | |
332 { | |
333 // if (e.op == TOKtype) | |
334 return new TypeExp(e.loc, s.getType()); | |
335 // return new DotTypeExp(e.loc, e, s); | |
336 } | |
337 | |
338 EnumMember em = s.isEnumMember(); | |
339 if (em) | |
340 { | |
341 assert(em.value); | |
342 return em.value.copy(); | |
343 } | |
344 | |
345 TemplateMixin tm = s.isTemplateMixin(); | |
346 if (tm) | |
347 { | |
348 Expression de = new DotExp(e.loc, e, new ScopeExp(e.loc, tm)); | |
349 de.type = e.type; | |
350 return de; | |
351 } | |
352 | |
353 TemplateDeclaration td = s.isTemplateDeclaration(); | |
354 if (td) | |
355 { | |
356 e = new DotTemplateExp(e.loc, e, td); | |
357 e.semantic(sc); | |
358 return e; | |
359 } | |
360 | |
361 TemplateInstance ti = s.isTemplateInstance(); | |
362 if (ti) | |
363 { | |
364 if (!ti.semanticRun) | |
365 ti.semantic(sc); | |
366 s = ti.inst.toAlias(); | |
367 if (!s.isTemplateInstance()) | |
368 goto L1; | |
369 Expression de = new DotExp(e.loc, e, new ScopeExp(e.loc, ti)); | |
370 de.type = e.type; | |
371 return de; | |
372 } | |
373 | |
374 OverloadSet o = s.isOverloadSet(); | |
375 if (o) | |
376 { | |
377 /* We really should allow this | |
378 */ | |
379 error(e.loc, "overload set for %s.%s not allowed in struct declaration", e.toChars(), ident.toChars()); | |
380 return new ErrorExp(); | |
381 } | |
382 | |
383 Declaration d = s.isDeclaration(); | |
384 if (!d) | |
385 { | |
386 e.error("%s.%s is not a declaration", e.toChars(), ident.toChars()); | |
387 return new ErrorExp(); | |
388 } | |
389 | |
390 if (e.op == TOK.TOKtype) | |
391 { | |
392 /* It's: | |
393 * Class.d | |
394 */ | |
395 if (d.isTupleDeclaration()) | |
396 { | |
397 e = new TupleExp(e.loc, d.isTupleDeclaration()); | |
398 e = e.semantic(sc); | |
399 return e; | |
400 } | |
73 | 401 else if (d.needThis() && (hasThis(sc) || !(sc.intypeof || d.isFuncDeclaration()))) |
0 | 402 { |
403 if (sc.func) | |
404 { | |
405 ClassDeclaration thiscd; | |
406 thiscd = sc.func.toParent().isClassDeclaration(); | |
407 | |
408 if (thiscd) | |
409 { | |
410 ClassDeclaration cd = e.type.isClassHandle(); | |
411 | |
412 if (cd is thiscd) | |
413 { | |
414 e = new ThisExp(e.loc); | |
415 e = new DotTypeExp(e.loc, e, cd); | |
416 DotVarExp de = new DotVarExp(e.loc, e, d); | |
417 e = de.semantic(sc); | |
418 return e; | |
419 } | |
420 else if ((!cd || !cd.isBaseOf(thiscd, null)) && !d.isFuncDeclaration()) | |
421 e.error("'this' is required, but %s is not a base class of %s", e.type.toChars(), thiscd.toChars()); | |
422 } | |
423 } | |
424 | |
425 /* Rewrite as: | |
426 * this.d | |
427 */ | |
428 DotVarExp de = new DotVarExp(e.loc, new ThisExp(e.loc), d); | |
429 e = de.semantic(sc); | |
430 return e; | |
431 } | |
432 else | |
433 { | |
434 VarExp ve = new VarExp(e.loc, d, 1); | |
435 return ve; | |
436 } | |
437 } | |
438 | |
439 if (d.isDataseg()) | |
440 { | |
441 // (e, d) | |
442 accessCheck(e.loc, sc, e, d); | |
443 VarExp ve = new VarExp(e.loc, d); | |
444 e = new CommaExp(e.loc, e, ve); | |
445 e.type = d.type; | |
446 return e; | |
447 } | |
448 | |
449 if (d.parent && d.toParent().isModule()) | |
450 { | |
451 // (e, d) | |
452 VarExp ve = new VarExp(e.loc, d, 1); | |
453 e = new CommaExp(e.loc, e, ve); | |
454 e.type = d.type; | |
455 return e; | |
456 } | |
457 | |
458 DotVarExp de = new DotVarExp(e.loc, e, d); | |
459 return de.semantic(sc); | |
460 } | |
461 | |
72 | 462 override ClassDeclaration isClassHandle() |
0 | 463 { |
464 return sym; | |
465 } | |
466 | |
72 | 467 override bool isBaseOf(Type t, int* poffset) |
0 | 468 { |
56 | 469 if (t.ty == Tclass) |
470 { | |
471 ClassDeclaration cd; | |
472 | |
473 cd = (cast(TypeClass)t).sym; | |
474 if (sym.isBaseOf(cd, poffset)) | |
475 return true; | |
476 } | |
477 | |
478 return false; | |
0 | 479 } |
480 | |
72 | 481 override MATCH implicitConvTo(Type to) |
0 | 482 { |
483 //printf("TypeClass.implicitConvTo(to = '%s') %s\n", to.toChars(), toChars()); | |
484 MATCH m = constConv(to); | |
485 if (m != MATCH.MATCHnomatch) | |
486 return m; | |
487 | |
488 ClassDeclaration cdto = to.isClassHandle(); | |
489 if (cdto && cdto.isBaseOf(sym, null)) | |
490 { | |
491 //printf("'to' is base\n"); | |
492 return MATCH.MATCHconvert; | |
493 } | |
494 | |
495 if (global.params.Dversion == 1) | |
496 { | |
497 // Allow conversion to (void *) | |
498 if (to.ty == TY.Tpointer && (cast(TypePointer)to).next.ty == TY.Tvoid) | |
499 return MATCH.MATCHconvert; | |
500 } | |
501 | |
502 m = MATCH.MATCHnomatch; | |
503 if (sym.aliasthis) | |
504 { | |
505 Declaration d = sym.aliasthis.isDeclaration(); | |
506 if (d) | |
507 { | |
508 assert(d.type); | |
509 Type t = d.type.addMod(mod); | |
510 m = t.implicitConvTo(to); | |
511 } | |
512 } | |
513 | |
514 return m; | |
515 } | |
516 | |
72 | 517 override Expression defaultInit(Loc loc) |
0 | 518 { |
519 version (LOGDEFAULTINIT) { | |
520 printf("TypeClass::defaultInit() '%s'\n", toChars()); | |
521 } | |
522 Expression e = new NullExp(loc); | |
523 e.type = this; | |
524 return e; | |
525 } | |
526 | |
72 | 527 override bool isZeroInit(Loc loc) |
0 | 528 { |
529 return true; | |
530 } | |
531 | |
72 | 532 override MATCH deduceType(Scope sc, Type tparam, TemplateParameters parameters, Objects dedtypes) |
0 | 533 { |
64 | 534 //printf("TypeClass.deduceType(this = %s)\n", toChars()); |
535 | |
536 /* If this class is a template class, and we're matching | |
537 * it against a template instance, convert the class type | |
538 * to a template instance, too, and try again. | |
539 */ | |
540 TemplateInstance ti = sym.parent.isTemplateInstance(); | |
541 | |
542 if (tparam && tparam.ty == Tinstance) | |
543 { | |
544 if (ti && ti.toAlias() == sym) | |
545 { | |
546 TypeInstance t = new TypeInstance(Loc(0), ti); | |
547 return t.deduceType(sc, tparam, parameters, dedtypes); | |
548 } | |
549 | |
550 /* Match things like: | |
551 * S!(T).foo | |
552 */ | |
553 TypeInstance tpi = cast(TypeInstance)tparam; | |
554 if (tpi.idents.dim) | |
555 { Identifier id = cast(Identifier)tpi.idents.data[tpi.idents.dim - 1]; | |
556 if (id.dyncast() == DYNCAST.DYNCAST_IDENTIFIER && sym.ident.equals(id)) | |
557 { | |
558 Type tparent = sym.parent.getType(); | |
559 if (tparent) | |
560 { | |
561 /* Slice off the .foo in S!(T).foo | |
562 */ | |
563 tpi.idents.dim--; | |
564 MATCH m = tparent.deduceType(sc, tpi, parameters, dedtypes); | |
565 tpi.idents.dim++; | |
566 return m; | |
567 } | |
568 } | |
569 } | |
570 } | |
571 | |
572 // Extra check | |
573 if (tparam && tparam.ty == Tclass) | |
574 { | |
575 TypeClass tp = cast(TypeClass)tparam; | |
576 | |
577 //printf("\t%d\n", (MATCH) implicitConvTo(tp)); | |
578 return implicitConvTo(tp); | |
579 } | |
580 return Type.deduceType(sc, tparam, parameters, dedtypes); | |
0 | 581 } |
582 | |
72 | 583 override bool isauto() |
0 | 584 { |
585 return sym.isauto; | |
586 } | |
587 | |
72 | 588 override bool checkBoolean() |
0 | 589 { |
590 return true; | |
591 } | |
592 | |
72 | 593 override TypeInfoDeclaration getTypeInfoDeclaration() |
0 | 594 { |
595 if (sym.isInterfaceDeclaration()) | |
596 return new TypeInfoInterfaceDeclaration(this); | |
597 else | |
598 return new TypeInfoClassDeclaration(this); | |
599 } | |
600 | |
72 | 601 override bool hasPointers() |
0 | 602 { |
603 return true; | |
604 } | |
605 | |
72 | 606 override bool builtinTypeInfo() |
0 | 607 { |
608 /* This is statically put out with the ClassInfo, so | |
609 * claim it is built in so it isn't regenerated by each module. | |
610 */ | |
611 version (DMDV2) { | |
612 return mod ? false : true; | |
613 } else { | |
614 return true; | |
615 } | |
616 } | |
617 | |
618 version (DMDV2) { | |
72 | 619 override Type toHeadMutable() |
0 | 620 { |
621 assert(false); | |
622 } | |
623 | |
72 | 624 override MATCH constConv(Type to) |
0 | 625 { |
626 if (equals(to)) | |
627 return MATCH.MATCHexact; | |
628 | |
629 if (ty == to.ty && sym == (cast(TypeClass)to).sym && to.mod == MOD.MODconst) | |
630 return MATCH.MATCHconst; | |
631 | |
632 return MATCH.MATCHnomatch; | |
633 } | |
634 | |
635 version (CPP_MANGLE) { | |
636 void toCppMangle(OutBuffer buf, CppMangleState* cms) | |
637 { | |
638 assert(false); | |
639 } | |
640 } | |
641 } | |
642 | |
72 | 643 override type* toCtype() |
0 | 644 { |
645 type* t; | |
646 Symbol* s; | |
647 | |
648 //printf("TypeClass.toCtype() %s\n", toChars()); | |
649 if (ctype) | |
650 return ctype; | |
651 | |
652 /* Need this symbol to do C++ name mangling | |
653 */ | |
654 string name = sym.isCPPinterface() ? sym.ident.toChars() : sym.toPrettyChars(); | |
655 s = symbol_calloc(toStringz(name)); | |
656 s.Sclass = SC.SCstruct; | |
657 s.Sstruct = struct_calloc(); | |
658 s.Sstruct.Sflags |= STR.STRclass; | |
659 s.Sstruct.Salignsize = sym.alignsize; | |
660 s.Sstruct.Sstructalign = cast(ubyte)sym.structalign; | |
661 s.Sstruct.Sstructsize = sym.structsize; | |
662 | |
663 t = type_alloc(TYM.TYstruct); | |
664 t.Ttag = cast(Classsym*)s; // structure tag name | |
665 t.Tcount++; | |
666 s.Stype = t; | |
667 slist_add(s); | |
668 | |
669 t = type_allocn(TYM.TYnptr, t); | |
670 | |
671 t.Tcount++; | |
672 ctype = t; | |
673 | |
674 /* Add in fields of the class | |
675 * (after setting ctype to avoid infinite recursion) | |
676 */ | |
677 if (global.params.symdebug) | |
678 for (int i = 0; i < sym.fields.dim; i++) | |
679 { | |
79 | 680 VarDeclaration v = cast(VarDeclaration)sym.fields[i]; |
0 | 681 |
682 Symbol* s2 = symbol_name(toStringz(v.ident.toChars()), SC.SCmember, v.type.toCtype()); | |
683 s2.Smemoff = v.offset; | |
684 list_append(&s.Sstruct.Sfldlst, s2); | |
685 } | |
686 | |
687 return t; | |
688 } | |
689 | |
72 | 690 override Symbol* toSymbol() |
0 | 691 { |
692 return sym.toSymbol(); | |
693 } | |
72 | 694 } |