1 /** 2 * Defines a `Dsymbol` representing an aggregate, which is a `struct`, `union` or `class`. 3 * 4 * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions), 5 * $(LINK2 https://dlang.org/spec/class.html, Class). 6 * 7 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved 8 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 9 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 10 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/aggregate.d, _aggregate.d) 11 * Documentation: https://dlang.org/phobos/dmd_aggregate.html 12 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aggregate.d 13 */ 14 15 module dmd.aggregate; 16 17 import core.stdc.stdio; 18 import core.checkedint; 19 20 import dmd.aliasthis; 21 import dmd.apply; 22 import dmd.arraytypes; 23 import dmd.astenums; 24 import dmd.declaration; 25 import dmd.dscope; 26 import dmd.dstruct; 27 import dmd.dsymbol; 28 import dmd.dsymbolsem; 29 import dmd.dtemplate; 30 import dmd.errors; 31 import dmd.expression; 32 import dmd.func; 33 import dmd.globals; 34 import dmd.id; 35 import dmd.identifier; 36 import dmd.mtype; 37 import dmd.tokens; 38 import dmd.typesem : defaultInit; 39 import dmd.visitor; 40 41 /** 42 * The ClassKind enum is used in AggregateDeclaration AST nodes to 43 * specify the linkage type of the struct/class/interface or if it 44 * is an anonymous class. If the class is anonymous it is also 45 * considered to be a D class. 46 */ 47 enum ClassKind : ubyte 48 { 49 /// the aggregate is a d(efault) class 50 d, 51 /// the aggregate is a C++ struct/class/interface 52 cpp, 53 /// the aggregate is an Objective-C class/interface 54 objc, 55 /// the aggregate is a C struct 56 c, 57 } 58 59 /** 60 * If an aggregate has a pargma(mangle, ...) this holds the information 61 * to mangle. 62 */ 63 struct MangleOverride 64 { 65 Dsymbol agg; // The symbol to copy template parameters from (if any) 66 Identifier id; // the name to override the aggregate's with, defaults to agg.ident 67 } 68 69 /*********************************************************** 70 * Abstract aggregate as a common ancestor for Class- and StructDeclaration. 71 */ 72 extern (C++) abstract class AggregateDeclaration : ScopeDsymbol 73 { 74 Type type; /// 75 StorageClass storage_class; /// 76 uint structsize; /// size of struct 77 uint alignsize; /// size of struct for alignment purposes 78 VarDeclarations fields; /// VarDeclaration fields 79 Dsymbol deferred; /// any deferred semantic2() or semantic3() symbol 80 81 /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface 82 ClassKind classKind; 83 /// Specify whether to mangle the aggregate as a `class` or a `struct` 84 /// This information is used by the MSVC mangler 85 /// Only valid for class and struct. TODO: Merge with ClassKind ? 86 CPPMANGLE cppmangle; 87 88 /// overridden symbol with pragma(mangle, "...") if not null 89 MangleOverride* mangleOverride; 90 91 /** 92 * !=null if is nested 93 * pointing to the dsymbol that directly enclosing it. 94 * 1. The function that enclosing it (nested struct and class) 95 * 2. The class that enclosing it (nested class only) 96 * 3. If enclosing aggregate is template, its enclosing dsymbol. 97 * 98 * See AggregateDeclaraton::makeNested for the details. 99 */ 100 Dsymbol enclosing; 101 102 VarDeclaration vthis; /// 'this' parameter if this aggregate is nested 103 VarDeclaration vthis2; /// 'this' parameter if this aggregate is a template and is nested 104 105 // Special member functions 106 FuncDeclarations invs; /// Array of invariants 107 FuncDeclaration inv; /// Merged invariant calling all members of invs 108 109 /// CtorDeclaration or TemplateDeclaration 110 Dsymbol ctor; 111 112 /// default constructor - should have no arguments, because 113 /// it would be stored in TypeInfo_Class.defaultConstructor 114 CtorDeclaration defaultCtor; 115 116 AliasThis aliasthis; /// forward unresolved lookups to aliasthis 117 118 DtorDeclarations dtors; /// Array of destructors 119 DtorDeclaration dtor; /// aggregate destructor calling dtors and member constructors 120 DtorDeclaration primaryDtor;/// non-deleting C++ destructor, same as dtor for D 121 DtorDeclaration tidtor; /// aggregate destructor used in TypeInfo (must have extern(D) ABI) 122 FuncDeclaration fieldDtor; /// aggregate destructor for just the fields 123 124 Expression getRTInfo; /// pointer to GC info generated by object.RTInfo(this) 125 126 /// 127 Visibility visibility; 128 bool noDefaultCtor; /// no default construction 129 bool disableNew; /// disallow allocations using `new` 130 Sizeok sizeok = Sizeok.none; /// set when structsize contains valid data 131 this(const ref Loc loc,Identifier id)132 final extern (D) this(const ref Loc loc, Identifier id) 133 { 134 super(loc, id); 135 visibility = Visibility(Visibility.Kind.public_); 136 } 137 138 /*************************************** 139 * Create a new scope from sc. 140 * semantic, semantic2 and semantic3 will use this for aggregate members. 141 */ newScope(Scope * sc)142 Scope* newScope(Scope* sc) 143 { 144 auto sc2 = sc.push(this); 145 sc2.stc &= STC.flowThruAggregate; 146 sc2.parent = this; 147 sc2.inunion = isUnionDeclaration(); 148 sc2.visibility = Visibility(Visibility.Kind.public_); 149 sc2.explicitVisibility = 0; 150 sc2.aligndecl = null; 151 sc2.userAttribDecl = null; 152 sc2.namespace = null; 153 return sc2; 154 } 155 setScope(Scope * sc)156 override final void setScope(Scope* sc) 157 { 158 // Might need a scope to resolve forward references. The check for 159 // semanticRun prevents unnecessary setting of _scope during deferred 160 // setScope phases for aggregates which already finished semantic(). 161 // See https://issues.dlang.org/show_bug.cgi?id=16607 162 if (semanticRun < PASS.semanticdone) 163 ScopeDsymbol.setScope(sc); 164 } 165 166 /*************************************** 167 * Returns: 168 * The total number of fields minus the number of hidden fields. 169 */ nonHiddenFields()170 final size_t nonHiddenFields() 171 { 172 return fields.dim - isNested() - (vthis2 !is null); 173 } 174 175 /*************************************** 176 * Collect all instance fields, then determine instance size. 177 * Returns: 178 * false if failed to determine the size. 179 */ determineSize(Loc loc)180 final bool determineSize(Loc loc) 181 { 182 //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok); 183 184 // The previous instance size finalizing had: 185 if (type.ty == Terror) 186 return false; // failed already 187 if (sizeok == Sizeok.done) 188 return true; // succeeded 189 190 if (!members) 191 { 192 error(loc, "unknown size"); 193 return false; 194 } 195 196 if (_scope) 197 dsymbolSemantic(this, null); 198 199 // Determine the instance size of base class first. 200 if (auto cd = isClassDeclaration()) 201 { 202 cd = cd.baseClass; 203 if (cd && !cd.determineSize(loc)) 204 goto Lfail; 205 } 206 207 // Determine instance fields when sizeok == Sizeok.none 208 if (!this.determineFields()) 209 goto Lfail; 210 if (sizeok != Sizeok.done) 211 finalizeSize(); 212 213 // this aggregate type has: 214 if (type.ty == Terror) 215 return false; // marked as invalid during the finalizing. 216 if (sizeok == Sizeok.done) 217 return true; // succeeded to calculate instance size. 218 219 Lfail: 220 // There's unresolvable forward reference. 221 if (type != Type.terror) 222 error(loc, "no size because of forward reference"); 223 // Don't cache errors from speculative semantic, might be resolvable later. 224 // https://issues.dlang.org/show_bug.cgi?id=16574 225 if (!global.gag) 226 { 227 type = Type.terror; 228 errors = true; 229 } 230 return false; 231 } 232 233 abstract void finalizeSize(); 234 size(const ref Loc loc)235 override final d_uns64 size(const ref Loc loc) 236 { 237 //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok); 238 bool ok = determineSize(loc); 239 //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok); 240 return ok ? structsize : SIZE_INVALID; 241 } 242 243 /*************************************** 244 * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit 245 * field initializers have unique memory space on instance. 246 * Returns: 247 * true if any errors happen. 248 */ checkOverlappedFields()249 extern (D) final bool checkOverlappedFields() 250 { 251 //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars()); 252 assert(sizeok == Sizeok.done); 253 size_t nfields = fields.dim; 254 if (isNested()) 255 { 256 auto cd = isClassDeclaration(); 257 if (!cd || !cd.baseClass || !cd.baseClass.isNested()) 258 nfields--; 259 if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2)) 260 nfields--; 261 } 262 bool errors = false; 263 264 // Fill in missing any elements with default initializers 265 foreach (i; 0 .. nfields) 266 { 267 auto vd = fields[i]; 268 if (vd.errors) 269 { 270 errors = true; 271 continue; 272 } 273 274 const vdIsVoidInit = vd._init && vd._init.isVoidInitializer(); 275 276 // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. 277 foreach (j; 0 .. nfields) 278 { 279 if (i == j) 280 continue; 281 auto v2 = fields[j]; 282 if (v2.errors) 283 { 284 errors = true; 285 continue; 286 } 287 if (!vd.isOverlappedWith(v2)) 288 continue; 289 290 // vd and v2 are overlapping. 291 vd.overlapped = true; 292 v2.overlapped = true; 293 294 if (!MODimplicitConv(vd.type.mod, v2.type.mod)) 295 v2.overlapUnsafe = true; 296 if (!MODimplicitConv(v2.type.mod, vd.type.mod)) 297 vd.overlapUnsafe = true; 298 299 if (i > j) 300 continue; 301 302 if (!v2._init) 303 continue; 304 305 if (v2._init.isVoidInitializer()) 306 continue; 307 308 if (vd._init && !vdIsVoidInit && v2._init) 309 { 310 .error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); 311 errors = true; 312 } 313 else if (v2._init && i < j) 314 { 315 .error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`", 316 v2.toChars(), v2._init.toChars(), vd.toChars()); 317 errors = true; 318 } 319 } 320 } 321 return errors; 322 } 323 324 /*************************************** 325 * Fill out remainder of elements[] with default initializers for fields[]. 326 * Params: 327 * loc = location 328 * elements = explicit arguments which given to construct object. 329 * ctorinit = true if the elements will be used for default initialization. 330 * Returns: 331 * false if any errors occur. 332 * Otherwise, returns true and the missing arguments will be pushed in elements[]. 333 */ fill(Loc loc,Expressions * elements,bool ctorinit)334 final bool fill(Loc loc, Expressions* elements, bool ctorinit) 335 { 336 //printf("AggregateDeclaration::fill() %s\n", toChars()); 337 assert(sizeok == Sizeok.done); 338 assert(elements); 339 const nfields = nonHiddenFields(); 340 bool errors = false; 341 342 size_t dim = elements.dim; 343 elements.setDim(nfields); 344 foreach (size_t i; dim .. nfields) 345 (*elements)[i] = null; 346 347 // Fill in missing any elements with default initializers 348 foreach (i; 0 .. nfields) 349 { 350 if ((*elements)[i]) 351 continue; 352 353 auto vd = fields[i]; 354 auto vx = vd; 355 if (vd._init && vd._init.isVoidInitializer()) 356 vx = null; 357 358 // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. 359 size_t fieldi = i; 360 foreach (j; 0 .. nfields) 361 { 362 if (i == j) 363 continue; 364 auto v2 = fields[j]; 365 if (!vd.isOverlappedWith(v2)) 366 continue; 367 368 if ((*elements)[j]) 369 { 370 vx = null; 371 break; 372 } 373 if (v2._init && v2._init.isVoidInitializer()) 374 continue; 375 376 version (all) 377 { 378 /* Prefer first found non-void-initialized field 379 * union U { int a; int b = 2; } 380 * U u; // Error: overlapping initialization for field a and b 381 */ 382 if (!vx) 383 { 384 vx = v2; 385 fieldi = j; 386 } 387 else if (v2._init) 388 { 389 .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); 390 errors = true; 391 } 392 } 393 else 394 { 395 // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always 396 397 /* Prefer explicitly initialized field 398 * union U { int a; int b = 2; } 399 * U u; // OK (u.b == 2) 400 */ 401 if (!vx || !vx._init && v2._init) 402 { 403 vx = v2; 404 fieldi = j; 405 } 406 else if (vx != vd && !vx.isOverlappedWith(v2)) 407 { 408 // Both vx and v2 fills vd, but vx and v2 does not overlap 409 } 410 else if (vx._init && v2._init) 411 { 412 .error(loc, "overlapping default initialization for field `%s` and `%s`", 413 v2.toChars(), vd.toChars()); 414 errors = true; 415 } 416 else 417 assert(vx._init || !vx._init && !v2._init); 418 } 419 } 420 if (vx) 421 { 422 Expression e; 423 if (vx.type.size() == 0) 424 { 425 e = null; 426 } 427 else if (vx._init) 428 { 429 assert(!vx._init.isVoidInitializer()); 430 if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057 431 { 432 vx.error(loc, "recursive initialization of field"); 433 errors = true; 434 } 435 else 436 e = vx.getConstInitializer(false); 437 } 438 else 439 { 440 if ((vx.storage_class & STC.nodefaultctor) && !ctorinit) 441 { 442 .error(loc, "field `%s.%s` must be initialized because it has no default constructor", 443 type.toChars(), vx.toChars()); 444 errors = true; 445 } 446 /* https://issues.dlang.org/show_bug.cgi?id=12509 447 * Get the element of static array type. 448 */ 449 Type telem = vx.type; 450 if (telem.ty == Tsarray) 451 { 452 /* We cannot use Type::baseElemOf() here. 453 * If the bottom of the Tsarray is an enum type, baseElemOf() 454 * will return the base of the enum, and its default initializer 455 * would be different from the enum's. 456 */ 457 TypeSArray tsa; 458 while ((tsa = telem.toBasetype().isTypeSArray()) !is null) 459 telem = tsa.next; 460 if (telem.ty == Tvoid) 461 telem = Type.tuns8.addMod(telem.mod); 462 } 463 if (telem.needsNested() && ctorinit) 464 e = telem.defaultInit(loc); 465 else 466 e = telem.defaultInitLiteral(loc); 467 } 468 (*elements)[fieldi] = e; 469 } 470 } 471 foreach (e; *elements) 472 { 473 if (e && e.op == TOK.error) 474 return false; 475 } 476 477 return !errors; 478 } 479 480 /**************************** 481 * Do byte or word alignment as necessary. 482 * Align sizes of 0, as we may not know array sizes yet. 483 * Params: 484 * alignment = struct alignment that is in effect 485 * size = alignment requirement of field 486 * poffset = pointer to offset to be aligned 487 */ alignmember(structalign_t alignment,uint size,uint * poffset)488 extern (D) static void alignmember(structalign_t alignment, uint size, uint* poffset) pure nothrow @safe 489 { 490 //printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset); 491 switch (alignment) 492 { 493 case cast(structalign_t)1: 494 // No alignment 495 break; 496 497 case cast(structalign_t)STRUCTALIGN_DEFAULT: 498 // Alignment in Target::fieldalignsize must match what the 499 // corresponding C compiler's default alignment behavior is. 500 assert(size > 0 && !(size & (size - 1))); 501 *poffset = (*poffset + size - 1) & ~(size - 1); 502 break; 503 504 default: 505 // Align on alignment boundary, which must be a positive power of 2 506 assert(alignment > 0 && !(alignment & (alignment - 1))); 507 *poffset = (*poffset + alignment - 1) & ~(alignment - 1); 508 break; 509 } 510 } 511 512 /**************************************** 513 * Place a member (mem) into an aggregate (agg), which can be a struct, union or class 514 * Returns: 515 * offset to place field at 516 * 517 * nextoffset: next location in aggregate 518 * memsize: size of member 519 * memalignsize: natural alignment of member 520 * alignment: alignment in effect for this member 521 * paggsize: size of aggregate (updated) 522 * paggalignsize: alignment of aggregate (updated) 523 * isunion: the aggregate is a union 524 */ placeField(uint * nextoffset,uint memsize,uint memalignsize,structalign_t alignment,uint * paggsize,uint * paggalignsize,bool isunion)525 extern (D) static uint placeField(uint* nextoffset, uint memsize, uint memalignsize, 526 structalign_t alignment, uint* paggsize, uint* paggalignsize, bool isunion) 527 { 528 uint ofs = *nextoffset; 529 530 const uint actualAlignment = 531 alignment == STRUCTALIGN_DEFAULT ? memalignsize : alignment; 532 533 // Ensure no overflow 534 bool overflow; 535 const sz = addu(memsize, actualAlignment, overflow); 536 addu(ofs, sz, overflow); 537 if (overflow) assert(0); 538 539 alignmember(alignment, memalignsize, &ofs); 540 uint memoffset = ofs; 541 ofs += memsize; 542 if (ofs > *paggsize) 543 *paggsize = ofs; 544 if (!isunion) 545 *nextoffset = ofs; 546 547 if (*paggalignsize < actualAlignment) 548 *paggalignsize = actualAlignment; 549 550 return memoffset; 551 } 552 getType()553 override final Type getType() 554 { 555 return type; 556 } 557 558 // is aggregate deprecated? isDeprecated()559 override final bool isDeprecated() const 560 { 561 return !!(this.storage_class & STC.deprecated_); 562 } 563 564 /// Flag this aggregate as deprecated setDeprecated()565 final void setDeprecated() 566 { 567 this.storage_class |= STC.deprecated_; 568 } 569 570 /**************************************** 571 * Returns true if there's an extra member which is the 'this' 572 * pointer to the enclosing context (enclosing aggregate or function) 573 */ isNested()574 final bool isNested() const 575 { 576 return enclosing !is null; 577 } 578 579 /* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested. 580 */ makeNested()581 extern (D) final void makeNested() 582 { 583 if (enclosing) // if already nested 584 return; 585 if (sizeok == Sizeok.done) 586 return; 587 if (isUnionDeclaration() || isInterfaceDeclaration()) 588 return; 589 if (storage_class & STC.static_) 590 return; 591 592 // If nested struct, add in hidden 'this' pointer to outer scope 593 auto s = toParentLocal(); 594 if (!s) 595 s = toParent2(); 596 if (!s) 597 return; 598 Type t = null; 599 if (auto fd = s.isFuncDeclaration()) 600 { 601 enclosing = fd; 602 603 /* https://issues.dlang.org/show_bug.cgi?id=14422 604 * If a nested class parent is a function, its 605 * context pointer (== `outer`) should be void* always. 606 */ 607 t = Type.tvoidptr; 608 } 609 else if (auto ad = s.isAggregateDeclaration()) 610 { 611 if (isClassDeclaration() && ad.isClassDeclaration()) 612 { 613 enclosing = ad; 614 } 615 else if (isStructDeclaration()) 616 { 617 if (auto ti = ad.parent.isTemplateInstance()) 618 { 619 enclosing = ti.enclosing; 620 } 621 } 622 t = ad.handleType(); 623 } 624 if (enclosing) 625 { 626 //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars()); 627 assert(t); 628 if (t.ty == Tstruct) 629 t = Type.tvoidptr; // t should not be a ref type 630 631 assert(!vthis); 632 vthis = new ThisDeclaration(loc, t); 633 //vthis.storage_class |= STC.ref_; 634 635 // Emulate vthis.addMember() 636 members.push(vthis); 637 638 // Emulate vthis.dsymbolSemantic() 639 vthis.storage_class |= STC.field; 640 vthis.parent = this; 641 vthis.visibility = Visibility(Visibility.Kind.public_); 642 vthis.alignment = t.alignment(); 643 vthis.semanticRun = PASS.semanticdone; 644 645 if (sizeok == Sizeok.fwd) 646 fields.push(vthis); 647 648 makeNested2(); 649 } 650 } 651 652 /* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer. 653 */ makeNested2()654 extern (D) final void makeNested2() 655 { 656 if (vthis2) 657 return; 658 if (!vthis) 659 makeNested(); // can't add second before first 660 if (!vthis) 661 return; 662 if (sizeok == Sizeok.done) 663 return; 664 if (isUnionDeclaration() || isInterfaceDeclaration()) 665 return; 666 if (storage_class & STC.static_) 667 return; 668 669 auto s0 = toParentLocal(); 670 auto s = toParent2(); 671 if (!s || !s0 || s == s0) 672 return; 673 auto cd = s.isClassDeclaration(); 674 Type t = cd ? cd.type : Type.tvoidptr; 675 676 vthis2 = new ThisDeclaration(loc, t); 677 //vthis2.storage_class |= STC.ref_; 678 679 // Emulate vthis2.addMember() 680 members.push(vthis2); 681 682 // Emulate vthis2.dsymbolSemantic() 683 vthis2.storage_class |= STC.field; 684 vthis2.parent = this; 685 vthis2.visibility = Visibility(Visibility.Kind.public_); 686 vthis2.alignment = t.alignment(); 687 vthis2.semanticRun = PASS.semanticdone; 688 689 if (sizeok == Sizeok.fwd) 690 fields.push(vthis2); 691 } 692 isExport()693 override final bool isExport() const 694 { 695 return visibility.kind == Visibility.Kind.export_; 696 } 697 698 /******************************************* 699 * Look for constructor declaration. 700 */ searchCtor()701 final Dsymbol searchCtor() 702 { 703 auto s = search(Loc.initial, Id.ctor); 704 if (s) 705 { 706 if (!(s.isCtorDeclaration() || 707 s.isTemplateDeclaration() || 708 s.isOverloadSet())) 709 { 710 s.error("is not a constructor; identifiers starting with `__` are reserved for the implementation"); 711 errors = true; 712 s = null; 713 } 714 } 715 if (s && s.toParent() != this) 716 s = null; // search() looks through ancestor classes 717 if (s) 718 { 719 // Finish all constructors semantics to determine this.noDefaultCtor. 720 struct SearchCtor 721 { 722 extern (C++) static int fp(Dsymbol s, void* ctxt) 723 { 724 auto f = s.isCtorDeclaration(); 725 if (f && f.semanticRun == PASS.init) 726 f.dsymbolSemantic(null); 727 return 0; 728 } 729 } 730 731 for (size_t i = 0; i < members.dim; i++) 732 { 733 auto sm = (*members)[i]; 734 sm.apply(&SearchCtor.fp, null); 735 } 736 } 737 return s; 738 } 739 visible()740 override final Visibility visible() pure nothrow @nogc @safe 741 { 742 return visibility; 743 } 744 745 // 'this' type handleType()746 final Type handleType() 747 { 748 return type; 749 } 750 751 // Does this class have an invariant function? hasInvariant()752 final bool hasInvariant() 753 { 754 return invs.length != 0; 755 } 756 757 // Back end 758 void* sinit; /// initializer symbol 759 inout(AggregateDeclaration)760 override final inout(AggregateDeclaration) isAggregateDeclaration() inout 761 { 762 return this; 763 } 764 accept(Visitor v)765 override void accept(Visitor v) 766 { 767 v.visit(this); 768 } 769 } 770