1 /**
2 * Defines a `class` declaration.
3 *
4 * Specification: $(LINK2 https://dlang.org/spec/class.html, Classes)
5 *
6 * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dclass.d, _dclass.d)
10 * Documentation: https://dlang.org/phobos/dmd_dclass.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dclass.d
12 */
13
14 module dmd.dclass;
15
16 import core.stdc.stdio;
17 import core.stdc.string;
18
19 import dmd.aggregate;
20 import dmd.apply;
21 import dmd.arraytypes;
22 import dmd.astenums;
23 import dmd.attrib;
24 import dmd.gluelayer;
25 import dmd.declaration;
26 import dmd.dscope;
27 import dmd.dsymbol;
28 import dmd.dsymbolsem;
29 import dmd.func;
30 import dmd.globals;
31 import dmd.id;
32 import dmd.identifier;
33 import dmd.mtype;
34 import dmd.objc;
35 import dmd.root.rmem;
36 import dmd.target;
37 import dmd.visitor;
38
39 /***********************************************************
40 */
41 extern (C++) struct BaseClass
42 {
43 Type type; // (before semantic processing)
44
45 ClassDeclaration sym;
46 uint offset; // 'this' pointer offset
47
48 // for interfaces: Array of FuncDeclaration's making up the vtbl[]
49 FuncDeclarations vtbl;
50
51 // if BaseClass is an interface, these
52 // are a copy of the InterfaceDeclaration.interfaces
53 BaseClass[] baseInterfaces;
54
thisBaseClass55 extern (D) this(Type type)
56 {
57 //printf("BaseClass(this = %p, '%s')\n", this, type.toChars());
58 this.type = type;
59 }
60
61 /****************************************
62 * Fill in vtbl[] for base class based on member functions of class cd.
63 * Input:
64 * vtbl if !=NULL, fill it in
65 * newinstance !=0 means all entries must be filled in by members
66 * of cd, not members of any base classes of cd.
67 * Returns:
68 * true if any entries were filled in by members of cd (not exclusively
69 * by base classes)
70 */
fillVtblBaseClass71 extern (C++) bool fillVtbl(ClassDeclaration cd, FuncDeclarations* vtbl, int newinstance)
72 {
73 bool result = false;
74
75 //printf("BaseClass.fillVtbl(this='%s', cd='%s')\n", sym.toChars(), cd.toChars());
76 if (vtbl)
77 vtbl.setDim(sym.vtbl.dim);
78
79 // first entry is ClassInfo reference
80 for (size_t j = sym.vtblOffset(); j < sym.vtbl.dim; j++)
81 {
82 FuncDeclaration ifd = sym.vtbl[j].isFuncDeclaration();
83
84 //printf(" vtbl[%d] is '%s'\n", j, ifd ? ifd.toChars() : "null");
85 assert(ifd);
86
87 // Find corresponding function in this class
88 auto tf = ifd.type.toTypeFunction();
89 auto fd = cd.findFunc(ifd.ident, tf);
90 if (fd && !fd.isAbstract())
91 {
92 if (fd.toParent() == cd)
93 result = true;
94 }
95 else
96 fd = null;
97 if (vtbl)
98 (*vtbl)[j] = fd;
99 }
100 return result;
101 }
102
copyBaseInterfacesBaseClass103 extern (D) void copyBaseInterfaces(BaseClasses* vtblInterfaces)
104 {
105 //printf("+copyBaseInterfaces(), %s\n", sym.toChars());
106 // if (baseInterfaces.length)
107 // return;
108 auto bc = cast(BaseClass*)mem.xcalloc(sym.interfaces.length, BaseClass.sizeof);
109 baseInterfaces = bc[0 .. sym.interfaces.length];
110 //printf("%s.copyBaseInterfaces()\n", sym.toChars());
111 for (size_t i = 0; i < baseInterfaces.length; i++)
112 {
113 BaseClass* b = &baseInterfaces[i];
114 BaseClass* b2 = sym.interfaces[i];
115
116 assert(b2.vtbl.dim == 0); // should not be filled yet
117 memcpy(b, b2, BaseClass.sizeof);
118
119 if (i) // single inheritance is i==0
120 vtblInterfaces.push(b); // only need for M.I.
121 b.copyBaseInterfaces(vtblInterfaces);
122 }
123 //printf("-copyBaseInterfaces\n");
124 }
125 }
126
127 enum ClassFlags : uint
128 {
129 none = 0x0,
130 isCOMclass = 0x1,
131 noPointers = 0x2,
132 hasOffTi = 0x4,
133 hasCtor = 0x8,
134 hasGetMembers = 0x10,
135 hasTypeInfo = 0x20,
136 isAbstract = 0x40,
137 isCPPclass = 0x80,
138 hasDtor = 0x100,
139 }
140
141 /***********************************************************
142 */
143 extern (C++) class ClassDeclaration : AggregateDeclaration
144 {
145 extern (C++) __gshared
146 {
147 // Names found by reading object.d in druntime
148 ClassDeclaration object;
149 ClassDeclaration throwable;
150 ClassDeclaration exception;
151 ClassDeclaration errorException;
152 ClassDeclaration cpp_type_info_ptr; // Object.__cpp_type_info_ptr
153 }
154
155 ClassDeclaration baseClass; // NULL only if this is Object
156 FuncDeclaration staticCtor;
157 FuncDeclaration staticDtor;
158 Dsymbols vtbl; // Array of FuncDeclaration's making up the vtbl[]
159 Dsymbols vtblFinal; // More FuncDeclaration's that aren't in vtbl[]
160
161 // Array of BaseClass's; first is super, rest are Interface's
162 BaseClasses* baseclasses;
163
164 /* Slice of baseclasses[] that does not include baseClass
165 */
166 BaseClass*[] interfaces;
167
168 // array of base interfaces that have their own vtbl[]
169 BaseClasses* vtblInterfaces;
170
171 // the ClassInfo object for this ClassDeclaration
172 TypeInfoClassDeclaration vclassinfo;
173
174 // true if this is a COM class
175 bool com;
176
177 /// true if this is a scope class
178 bool stack;
179
180 /// if this is a C++ class, this is the slot reserved for the virtual destructor
181 int cppDtorVtblIndex = -1;
182
183 /// to prevent recursive attempts
184 private bool inuse;
185
186 ThreeState isabstract;
187
188 /// set the progress of base classes resolving
189 Baseok baseok;
190
191 /**
192 * Data for a class declaration that is needed for the Objective-C
193 * integration.
194 */
195 ObjcClassDeclaration objc;
196
197 Symbol* cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr
198
this(const ref Loc loc,Identifier id,BaseClasses * baseclasses,Dsymbols * members,bool inObject)199 final extern (D) this(const ref Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject)
200 {
201 objc = ObjcClassDeclaration(this);
202
203 if (!id)
204 id = Identifier.generateAnonymousId("class");
205
206 super(loc, id);
207
208 static immutable msg = "only object.d can define this reserved class name";
209
210 if (baseclasses)
211 {
212 // Actually, this is a transfer
213 this.baseclasses = baseclasses;
214 }
215 else
216 this.baseclasses = new BaseClasses();
217
218 this.members = members;
219
220 //printf("ClassDeclaration(%s), dim = %d\n", ident.toChars(), this.baseclasses.dim);
221
222 // For forward references
223 type = new TypeClass(this);
224
225 // Look for special class names
226 if (id == Id.__sizeof || id == Id.__xalignof || id == Id._mangleof)
227 error("illegal class name");
228
229 // BUG: What if this is the wrong TypeInfo, i.e. it is nested?
230 if (id.toChars()[0] == 'T')
231 {
232 if (id == Id.TypeInfo)
233 {
234 if (!inObject)
235 error("%s", msg.ptr);
236 Type.dtypeinfo = this;
237 }
238 if (id == Id.TypeInfo_Class)
239 {
240 if (!inObject)
241 error("%s", msg.ptr);
242 Type.typeinfoclass = this;
243 }
244 if (id == Id.TypeInfo_Interface)
245 {
246 if (!inObject)
247 error("%s", msg.ptr);
248 Type.typeinfointerface = this;
249 }
250 if (id == Id.TypeInfo_Struct)
251 {
252 if (!inObject)
253 error("%s", msg.ptr);
254 Type.typeinfostruct = this;
255 }
256 if (id == Id.TypeInfo_Pointer)
257 {
258 if (!inObject)
259 error("%s", msg.ptr);
260 Type.typeinfopointer = this;
261 }
262 if (id == Id.TypeInfo_Array)
263 {
264 if (!inObject)
265 error("%s", msg.ptr);
266 Type.typeinfoarray = this;
267 }
268 if (id == Id.TypeInfo_StaticArray)
269 {
270 //if (!inObject)
271 // Type.typeinfostaticarray.error("%s", msg);
272 Type.typeinfostaticarray = this;
273 }
274 if (id == Id.TypeInfo_AssociativeArray)
275 {
276 if (!inObject)
277 error("%s", msg.ptr);
278 Type.typeinfoassociativearray = this;
279 }
280 if (id == Id.TypeInfo_Enum)
281 {
282 if (!inObject)
283 error("%s", msg.ptr);
284 Type.typeinfoenum = this;
285 }
286 if (id == Id.TypeInfo_Function)
287 {
288 if (!inObject)
289 error("%s", msg.ptr);
290 Type.typeinfofunction = this;
291 }
292 if (id == Id.TypeInfo_Delegate)
293 {
294 if (!inObject)
295 error("%s", msg.ptr);
296 Type.typeinfodelegate = this;
297 }
298 if (id == Id.TypeInfo_Tuple)
299 {
300 if (!inObject)
301 error("%s", msg.ptr);
302 Type.typeinfotypelist = this;
303 }
304 if (id == Id.TypeInfo_Const)
305 {
306 if (!inObject)
307 error("%s", msg.ptr);
308 Type.typeinfoconst = this;
309 }
310 if (id == Id.TypeInfo_Invariant)
311 {
312 if (!inObject)
313 error("%s", msg.ptr);
314 Type.typeinfoinvariant = this;
315 }
316 if (id == Id.TypeInfo_Shared)
317 {
318 if (!inObject)
319 error("%s", msg.ptr);
320 Type.typeinfoshared = this;
321 }
322 if (id == Id.TypeInfo_Wild)
323 {
324 if (!inObject)
325 error("%s", msg.ptr);
326 Type.typeinfowild = this;
327 }
328 if (id == Id.TypeInfo_Vector)
329 {
330 if (!inObject)
331 error("%s", msg.ptr);
332 Type.typeinfovector = this;
333 }
334 }
335
336 if (id == Id.Object)
337 {
338 if (!inObject)
339 error("%s", msg.ptr);
340 object = this;
341 }
342
343 if (id == Id.Throwable)
344 {
345 if (!inObject)
346 error("%s", msg.ptr);
347 throwable = this;
348 }
349 if (id == Id.Exception)
350 {
351 if (!inObject)
352 error("%s", msg.ptr);
353 exception = this;
354 }
355 if (id == Id.Error)
356 {
357 if (!inObject)
358 error("%s", msg.ptr);
359 errorException = this;
360 }
361 if (id == Id.cpp_type_info_ptr)
362 {
363 if (!inObject)
364 error("%s", msg.ptr);
365 cpp_type_info_ptr = this;
366 }
367
368 baseok = Baseok.none;
369 }
370
create(const ref Loc loc,Identifier id,BaseClasses * baseclasses,Dsymbols * members,bool inObject)371 static ClassDeclaration create(const ref Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject)
372 {
373 return new ClassDeclaration(loc, id, baseclasses, members, inObject);
374 }
375
376 override const(char)* toPrettyChars(bool qualifyTypes = false)
377 {
378 if (objc.isMeta)
379 return .objc.toPrettyChars(this, qualifyTypes);
380
381 return super.toPrettyChars(qualifyTypes);
382 }
383
syntaxCopy(Dsymbol s)384 override ClassDeclaration syntaxCopy(Dsymbol s)
385 {
386 //printf("ClassDeclaration.syntaxCopy('%s')\n", toChars());
387 ClassDeclaration cd =
388 s ? cast(ClassDeclaration)s
389 : new ClassDeclaration(loc, ident, null, null, false);
390
391 cd.storage_class |= storage_class;
392
393 cd.baseclasses.setDim(this.baseclasses.dim);
394 for (size_t i = 0; i < cd.baseclasses.dim; i++)
395 {
396 BaseClass* b = (*this.baseclasses)[i];
397 auto b2 = new BaseClass(b.type.syntaxCopy());
398 (*cd.baseclasses)[i] = b2;
399 }
400
401 ScopeDsymbol.syntaxCopy(cd);
402 return cd;
403 }
404
newScope(Scope * sc)405 override Scope* newScope(Scope* sc)
406 {
407 auto sc2 = super.newScope(sc);
408 if (isCOMclass())
409 {
410 /* This enables us to use COM objects under Linux and
411 * work with things like XPCOM
412 */
413 sc2.linkage = target.systemLinkage();
414 }
415 return sc2;
416 }
417
418 /*********************************************
419 * Determine if 'this' is a base class of cd.
420 * This is used to detect circular inheritance only.
421 */
isBaseOf2(ClassDeclaration cd)422 final bool isBaseOf2(ClassDeclaration cd) pure nothrow @nogc
423 {
424 if (!cd)
425 return false;
426 //printf("ClassDeclaration.isBaseOf2(this = '%s', cd = '%s')\n", toChars(), cd.toChars());
427 for (size_t i = 0; i < cd.baseclasses.dim; i++)
428 {
429 BaseClass* b = (*cd.baseclasses)[i];
430 if (b.sym == this || isBaseOf2(b.sym))
431 return true;
432 }
433 return false;
434 }
435
436 enum OFFSET_RUNTIME = 0x76543210;
437 enum OFFSET_FWDREF = 0x76543211;
438
439 /*******************************************
440 * Determine if 'this' is a base class of cd.
441 */
isBaseOf(ClassDeclaration cd,int * poffset)442 bool isBaseOf(ClassDeclaration cd, int* poffset) pure nothrow @nogc
443 {
444 //printf("ClassDeclaration.isBaseOf(this = '%s', cd = '%s')\n", toChars(), cd.toChars());
445 if (poffset)
446 *poffset = 0;
447 while (cd)
448 {
449 assert(cd.baseClass || cd.semanticRun >= PASS.semanticdone || cd.isInterfaceDeclaration());
450 if (this == cd.baseClass)
451 return true;
452
453 cd = cd.baseClass;
454 }
455 return false;
456 }
457
458 /*********************************************
459 * Determine if 'this' has complete base class information.
460 * This is used to detect forward references in covariant overloads.
461 */
isBaseInfoComplete()462 final bool isBaseInfoComplete() const
463 {
464 return baseok >= Baseok.done;
465 }
466
467 override final Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
468 {
469 //printf("%s.ClassDeclaration.search('%s', flags=x%x)\n", toChars(), ident.toChars(), flags);
470 //if (_scope) printf("%s baseok = %d\n", toChars(), baseok);
471 if (_scope && baseok < Baseok.done)
472 {
473 if (!inuse)
474 {
475 // must semantic on base class/interfaces
476 inuse = true;
477 dsymbolSemantic(this, null);
478 inuse = false;
479 }
480 }
481
482 if (!members || !symtab) // opaque or addMember is not yet done
483 {
484 // .stringof is always defined (but may be hidden by some other symbol)
485 if (ident != Id.stringof && !(flags & IgnoreErrors) && semanticRun < PASS.semanticdone)
486 error("is forward referenced when looking for `%s`", ident.toChars());
487 //*(char*)0=0;
488 return null;
489 }
490
491 auto s = ScopeDsymbol.search(loc, ident, flags);
492
493 // don't search imports of base classes
494 if (flags & SearchImportsOnly)
495 return s;
496
497 if (s)
498 return s;
499
500 // Search bases classes in depth-first, left to right order
foreach(b;(* baseclasses)[])501 foreach (b; (*baseclasses)[])
502 {
503 if (!b.sym)
504 continue;
505
506 if (!b.sym.symtab)
507 {
508 error("base `%s` is forward referenced", b.sym.ident.toChars());
509 continue;
510 }
511
512 import dmd.access : symbolIsVisible;
513
514 s = b.sym.search(loc, ident, flags);
515 if (!s)
516 continue;
517 else if (s == this) // happens if s is nested in this and derives from this
518 s = null;
519 else if (!(flags & IgnoreSymbolVisibility) && !(s.visible().kind == Visibility.Kind.protected_) && !symbolIsVisible(this, s))
520 s = null;
521 else
522 break;
523 }
524
525 return s;
526 }
527
528 /************************************
529 * Search base classes in depth-first, left-to-right order for
530 * a class or interface named 'ident'.
531 * Stops at first found. Does not look for additional matches.
532 * Params:
533 * ident = identifier to search for
534 * Returns:
535 * ClassDeclaration if found, null if not
536 */
searchBase(Identifier ident)537 final ClassDeclaration searchBase(Identifier ident)
538 {
539 foreach (b; *baseclasses)
540 {
541 auto cdb = b.type.isClassHandle();
542 if (!cdb) // https://issues.dlang.org/show_bug.cgi?id=10616
543 return null;
544 if (cdb.ident.equals(ident))
545 return cdb;
546 auto result = cdb.searchBase(ident);
547 if (result)
548 return result;
549 }
550 return null;
551 }
552
finalizeSize()553 final override void finalizeSize()
554 {
555 assert(sizeok != Sizeok.done);
556
557 // Set the offsets of the fields and determine the size of the class
558 if (baseClass)
559 {
560 assert(baseClass.sizeok == Sizeok.done);
561
562 alignsize = baseClass.alignsize;
563 if (classKind == ClassKind.cpp)
564 structsize = target.cpp.derivedClassOffset(baseClass);
565 else
566 structsize = baseClass.structsize;
567 }
568 else if (classKind == ClassKind.objc)
569 structsize = 0; // no hidden member for an Objective-C class
570 else if (isInterfaceDeclaration())
571 {
572 if (interfaces.length == 0)
573 {
574 alignsize = target.ptrsize;
575 structsize = target.ptrsize; // allow room for __vptr
576 }
577 }
578 else
579 {
580 alignsize = target.ptrsize;
581 structsize = target.ptrsize; // allow room for __vptr
582 if (hasMonitor())
583 structsize += target.ptrsize; // allow room for __monitor
584 }
585
586 //printf("finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
587 size_t bi = 0; // index into vtblInterfaces[]
588
589 /****
590 * Runs through the inheritance graph to set the BaseClass.offset fields.
591 * Recursive in order to account for the size of the interface classes, if they are
592 * more than just interfaces.
593 * Params:
594 * cd = interface to look at
595 * baseOffset = offset of where cd will be placed
596 * Returns:
597 * subset of instantiated size used by cd for interfaces
598 */
599 uint membersPlace(ClassDeclaration cd, uint baseOffset)
600 {
601 //printf(" membersPlace(%s, %d)\n", cd.toChars(), baseOffset);
602 uint offset = baseOffset;
603
604 foreach (BaseClass* b; cd.interfaces)
605 {
606 if (b.sym.sizeok != Sizeok.done)
607 b.sym.finalizeSize();
608 assert(b.sym.sizeok == Sizeok.done);
609
610 if (!b.sym.alignsize)
611 b.sym.alignsize = target.ptrsize;
612 alignmember(structalign_t(cast(ushort)b.sym.alignsize), b.sym.alignsize, &offset);
613 assert(bi < vtblInterfaces.dim);
614
615 BaseClass* bv = (*vtblInterfaces)[bi];
616 if (b.sym.interfaces.length == 0)
617 {
618 //printf("\tvtblInterfaces[%d] b=%p b.sym = %s, offset = %d\n", bi, bv, bv.sym.toChars(), offset);
619 bv.offset = offset;
620 ++bi;
621 // All the base interfaces down the left side share the same offset
622 for (BaseClass* b2 = bv; b2.baseInterfaces.length; )
623 {
624 b2 = &b2.baseInterfaces[0];
625 b2.offset = offset;
626 //printf("\tvtblInterfaces[%d] b=%p sym = %s, offset = %d\n", bi, b2, b2.sym.toChars(), b2.offset);
627 }
628 }
629 membersPlace(b.sym, offset);
630 //printf(" %s size = %d\n", b.sym.toChars(), b.sym.structsize);
631 offset += b.sym.structsize;
632 if (alignsize < b.sym.alignsize)
633 alignsize = b.sym.alignsize;
634 }
635 return offset - baseOffset;
636 }
637
638 structsize += membersPlace(this, structsize);
639
640 if (isInterfaceDeclaration())
641 {
642 sizeok = Sizeok.done;
643 return;
644 }
645
646 // FIXME: Currently setFieldOffset functions need to increase fields
647 // to calculate each variable offsets. It can be improved later.
648 fields.setDim(0);
649
650 FieldState fieldState;
651 fieldState.offset = structsize;
652 foreach (s; *members)
653 {
654 s.setFieldOffset(this, fieldState, false);
655 }
656
657 sizeok = Sizeok.done;
658
659 // Calculate fields[i].overlapped
660 checkOverlappedFields();
661 }
662
663 /**************
664 * Returns: true if there's a __monitor field
665 */
hasMonitor()666 final bool hasMonitor()
667 {
668 return classKind == ClassKind.d;
669 }
670
isFuncHidden(FuncDeclaration fd)671 final bool isFuncHidden(FuncDeclaration fd)
672 {
673 //printf("ClassDeclaration.isFuncHidden(class = %s, fd = %s)\n", toChars(), fd.toPrettyChars());
674 Dsymbol s = search(Loc.initial, fd.ident, IgnoreAmbiguous | IgnoreErrors);
675 if (!s)
676 {
677 //printf("not found\n");
678 /* Because, due to a hack, if there are multiple definitions
679 * of fd.ident, NULL is returned.
680 */
681 return false;
682 }
683 s = s.toAlias();
684 if (auto os = s.isOverloadSet())
685 {
686 foreach (sm; os.a)
687 {
688 auto fm = sm.isFuncDeclaration();
689 if (overloadApply(fm, s => fd == s.isFuncDeclaration()))
690 return false;
691 }
692 return true;
693 }
694 else
695 {
696 auto f = s.isFuncDeclaration();
697 //printf("%s fdstart = %p\n", s.kind(), fdstart);
698 if (overloadApply(f, s => fd == s.isFuncDeclaration()))
699 return false;
700 return !fd.parent.isTemplateMixin();
701 }
702 }
703
704 /****************
705 * Find virtual function matching identifier and type.
706 * Used to build virtual function tables for interface implementations.
707 * Params:
708 * ident = function's identifier
709 * tf = function's type
710 * Returns:
711 * function symbol if found, null if not
712 * Errors:
713 * prints error message if more than one match
714 */
findFunc(Identifier ident,TypeFunction tf)715 final FuncDeclaration findFunc(Identifier ident, TypeFunction tf)
716 {
717 //printf("ClassDeclaration.findFunc(%s, %s) %s\n", ident.toChars(), tf.toChars(), toChars());
718 FuncDeclaration fdmatch = null;
719 FuncDeclaration fdambig = null;
720
721 void updateBestMatch(FuncDeclaration fd)
722 {
723 fdmatch = fd;
724 fdambig = null;
725 //printf("Lfd fdmatch = %s %s [%s]\n", fdmatch.toChars(), fdmatch.type.toChars(), fdmatch.loc.toChars());
726 }
727
728 void searchVtbl(ref Dsymbols vtbl)
729 {
730 bool seenInterfaceVirtual;
731 foreach (s; vtbl)
732 {
733 auto fd = s.isFuncDeclaration();
734 if (!fd)
735 continue;
736
737 // the first entry might be a ClassInfo
738 //printf("\t[%d] = %s\n", i, fd.toChars());
739 if (ident != fd.ident || fd.type.covariant(tf) != Covariant.yes)
740 {
741 //printf("\t\t%d\n", fd.type.covariant(tf));
742 continue;
743 }
744
745 //printf("fd.parent.isClassDeclaration() = %p\n", fd.parent.isClassDeclaration());
746 if (!fdmatch)
747 {
748 updateBestMatch(fd);
749 continue;
750 }
751 if (fd == fdmatch)
752 continue;
753
754 /* Functions overriding interface functions for extern(C++) with VC++
755 * are not in the normal vtbl, but in vtblFinal. If the implementation
756 * is again overridden in a child class, both would be found here.
757 * The function in the child class should override the function
758 * in the base class, which is done here, because searchVtbl is first
759 * called for the child class. Checking seenInterfaceVirtual makes
760 * sure, that the compared functions are not in the same vtbl.
761 */
762 if (fd.interfaceVirtual &&
763 fd.interfaceVirtual is fdmatch.interfaceVirtual &&
764 !seenInterfaceVirtual &&
765 fdmatch.type.covariant(fd.type) == Covariant.yes)
766 {
767 seenInterfaceVirtual = true;
768 continue;
769 }
770
771 {
772 // Function type matching: exact > covariant
773 MATCH m1 = tf.equals(fd.type) ? MATCH.exact : MATCH.nomatch;
774 MATCH m2 = tf.equals(fdmatch.type) ? MATCH.exact : MATCH.nomatch;
775 if (m1 > m2)
776 {
777 updateBestMatch(fd);
778 continue;
779 }
780 else if (m1 < m2)
781 continue;
782 }
783 {
784 MATCH m1 = (tf.mod == fd.type.mod) ? MATCH.exact : MATCH.nomatch;
785 MATCH m2 = (tf.mod == fdmatch.type.mod) ? MATCH.exact : MATCH.nomatch;
786 if (m1 > m2)
787 {
788 updateBestMatch(fd);
789 continue;
790 }
791 else if (m1 < m2)
792 continue;
793 }
794 {
795 // The way of definition: non-mixin > mixin
796 MATCH m1 = fd.parent.isClassDeclaration() ? MATCH.exact : MATCH.nomatch;
797 MATCH m2 = fdmatch.parent.isClassDeclaration() ? MATCH.exact : MATCH.nomatch;
798 if (m1 > m2)
799 {
800 updateBestMatch(fd);
801 continue;
802 }
803 else if (m1 < m2)
804 continue;
805 }
806
807 fdambig = fd;
808 //printf("Lambig fdambig = %s %s [%s]\n", fdambig.toChars(), fdambig.type.toChars(), fdambig.loc.toChars());
809 }
810 }
811
812 searchVtbl(vtbl);
813 for (auto cd = this; cd; cd = cd.baseClass)
814 {
815 searchVtbl(cd.vtblFinal);
816 }
817
818 if (fdambig)
819 error("ambiguous virtual function `%s`", fdambig.toChars());
820
821 return fdmatch;
822 }
823
824 /****************************************
825 */
isCOMclass()826 final bool isCOMclass() const
827 {
828 return com;
829 }
830
isCOMinterface()831 bool isCOMinterface() const
832 {
833 return false;
834 }
835
isCPPclass()836 final bool isCPPclass() const
837 {
838 return classKind == ClassKind.cpp;
839 }
840
isCPPinterface()841 bool isCPPinterface() const
842 {
843 return false;
844 }
845
846 /****************************************
847 */
isAbstract()848 final bool isAbstract()
849 {
850 enum log = false;
851 if (isabstract != ThreeState.none)
852 return isabstract == ThreeState.yes;
853
854 if (log) printf("isAbstract(%s)\n", toChars());
855
856 bool no() { if (log) printf("no\n"); isabstract = ThreeState.no; return false; }
857 bool yes() { if (log) printf("yes\n"); isabstract = ThreeState.yes; return true; }
858
859 if (storage_class & STC.abstract_ || _scope && _scope.stc & STC.abstract_)
860 return yes();
861
862 if (errors)
863 return no();
864
865 /* https://issues.dlang.org/show_bug.cgi?id=11169
866 * Resolve forward references to all class member functions,
867 * and determine whether this class is abstract.
868 */
869 static int func(Dsymbol s)
870 {
871 auto fd = s.isFuncDeclaration();
872 if (!fd)
873 return 0;
874 if (fd.storage_class & STC.static_)
875 return 0;
876
877 if (fd.isAbstract())
878 return 1;
879 return 0;
880 }
881
882 for (size_t i = 0; i < members.dim; i++)
883 {
884 auto s = (*members)[i];
885 if (s.apply(&func))
886 {
887 return yes();
888 }
889 }
890
891 /* If the base class is not abstract, then this class cannot
892 * be abstract.
893 */
894 if (!isInterfaceDeclaration() && (!baseClass || !baseClass.isAbstract()))
895 return no();
896
897 /* If any abstract functions are inherited, but not overridden,
898 * then the class is abstract. Do this by checking the vtbl[].
899 * Need to do semantic() on class to fill the vtbl[].
900 */
901 this.dsymbolSemantic(null);
902
903 /* The next line should work, but does not because when ClassDeclaration.dsymbolSemantic()
904 * is called recursively it can set PASS.semanticdone without finishing it.
905 */
906 //if (semanticRun < PASS.semanticdone)
907 {
908 /* Could not complete semantic(). Try running semantic() on
909 * each of the virtual functions,
910 * which will fill in the vtbl[] overrides.
911 */
912 static int virtualSemantic(Dsymbol s)
913 {
914 auto fd = s.isFuncDeclaration();
915 if (fd && !(fd.storage_class & STC.static_) && !fd.isUnitTestDeclaration())
916 fd.dsymbolSemantic(null);
917 return 0;
918 }
919
920 for (size_t i = 0; i < members.dim; i++)
921 {
922 auto s = (*members)[i];
923 s.apply(&virtualSemantic);
924 }
925 }
926
927 /* Finally, check the vtbl[]
928 */
929 foreach (i; 1 .. vtbl.dim)
930 {
931 auto fd = vtbl[i].isFuncDeclaration();
932 //if (fd) printf("\tvtbl[%d] = [%s] %s\n", i, fd.loc.toChars(), fd.toPrettyChars());
933 if (!fd || fd.isAbstract())
934 {
935 return yes();
936 }
937 }
938
939 return no();
940 }
941
942 /****************************************
943 * Determine if slot 0 of the vtbl[] is reserved for something else.
944 * For class objects, yes, this is where the classinfo ptr goes.
945 * For COM interfaces, no.
946 * For non-COM interfaces, yes, this is where the Interface ptr goes.
947 * Returns:
948 * 0 vtbl[0] is first virtual function pointer
949 * 1 vtbl[0] is classinfo/interfaceinfo pointer
950 */
vtblOffset()951 int vtblOffset() const
952 {
953 return classKind == ClassKind.cpp ? 0 : 1;
954 }
955
956 /****************************************
957 */
kind()958 override const(char)* kind() const
959 {
960 return "class";
961 }
962
963 /****************************************
964 */
addLocalClass(ClassDeclarations * aclasses)965 override final void addLocalClass(ClassDeclarations* aclasses)
966 {
967 if (classKind != ClassKind.objc)
968 aclasses.push(this);
969 }
970
addObjcSymbols(ClassDeclarations * classes,ClassDeclarations * categories)971 override final void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories)
972 {
973 .objc.addSymbols(this, classes, categories);
974 }
975
976 // Back end
977 Dsymbol vtblsym;
978
vtblSymbol()979 final Dsymbol vtblSymbol()
980 {
981 if (!vtblsym)
982 {
983 auto vtype = Type.tvoidptr.immutableOf().sarrayOf(vtbl.dim);
984 auto var = new VarDeclaration(loc, vtype, Identifier.idPool("__vtbl"), null, STC.immutable_ | STC.static_);
985 var.addMember(null, this);
986 var.isdataseg = 1;
987 var._linkage = LINK.d;
988 var.semanticRun = PASS.semanticdone; // no more semantic wanted
989 vtblsym = var;
990 }
991 return vtblsym;
992 }
993
inout(ClassDeclaration)994 override final inout(ClassDeclaration) isClassDeclaration() inout @nogc nothrow pure @safe
995 {
996 return this;
997 }
998
accept(Visitor v)999 override void accept(Visitor v)
1000 {
1001 v.visit(this);
1002 }
1003 }
1004
1005 /***********************************************************
1006 */
1007 extern (C++) final class InterfaceDeclaration : ClassDeclaration
1008 {
this(const ref Loc loc,Identifier id,BaseClasses * baseclasses)1009 extern (D) this(const ref Loc loc, Identifier id, BaseClasses* baseclasses)
1010 {
1011 super(loc, id, baseclasses, null, false);
1012 if (id == Id.IUnknown) // IUnknown is the root of all COM interfaces
1013 {
1014 com = true;
1015 classKind = ClassKind.cpp; // IUnknown is also a C++ interface
1016 }
1017 }
1018
syntaxCopy(Dsymbol s)1019 override InterfaceDeclaration syntaxCopy(Dsymbol s)
1020 {
1021 InterfaceDeclaration id =
1022 s ? cast(InterfaceDeclaration)s
1023 : new InterfaceDeclaration(loc, ident, null);
1024 ClassDeclaration.syntaxCopy(id);
1025 return id;
1026 }
1027
1028
newScope(Scope * sc)1029 override Scope* newScope(Scope* sc)
1030 {
1031 auto sc2 = super.newScope(sc);
1032 if (com)
1033 sc2.linkage = LINK.windows;
1034 else if (classKind == ClassKind.cpp)
1035 sc2.linkage = LINK.cpp;
1036 else if (classKind == ClassKind.objc)
1037 sc2.linkage = LINK.objc;
1038 return sc2;
1039 }
1040
1041 /*******************************************
1042 * Determine if 'this' is a base class of cd.
1043 * (Actually, if it is an interface supported by cd)
1044 * Output:
1045 * *poffset offset to start of class
1046 * OFFSET_RUNTIME must determine offset at runtime
1047 * Returns:
1048 * false not a base
1049 * true is a base
1050 */
isBaseOf(ClassDeclaration cd,int * poffset)1051 override bool isBaseOf(ClassDeclaration cd, int* poffset) pure nothrow @nogc
1052 {
1053 //printf("%s.InterfaceDeclaration.isBaseOf(cd = '%s')\n", toChars(), cd.toChars());
1054 assert(!baseClass);
1055 foreach (b; cd.interfaces)
1056 {
1057 //printf("\tX base %s\n", b.sym.toChars());
1058 if (this == b.sym)
1059 {
1060 //printf("\tfound at offset %d\n", b.offset);
1061 if (poffset)
1062 {
1063 // don't return incorrect offsets
1064 // https://issues.dlang.org/show_bug.cgi?id=16980
1065 *poffset = cd.sizeok == Sizeok.done ? b.offset : OFFSET_FWDREF;
1066 }
1067 // printf("\tfound at offset %d\n", b.offset);
1068 return true;
1069 }
1070 if (baseClassImplementsInterface(this, b, poffset))
1071 return true;
1072 }
1073 if (cd.baseClass && isBaseOf(cd.baseClass, poffset))
1074 return true;
1075
1076 if (poffset)
1077 *poffset = 0;
1078 return false;
1079 }
1080
1081 /*******************************************
1082 */
kind()1083 override const(char)* kind() const
1084 {
1085 return "interface";
1086 }
1087
1088 /****************************************
1089 * Determine if slot 0 of the vtbl[] is reserved for something else.
1090 * For class objects, yes, this is where the ClassInfo ptr goes.
1091 * For COM interfaces, no.
1092 * For non-COM interfaces, yes, this is where the Interface ptr goes.
1093 */
vtblOffset()1094 override int vtblOffset() const
1095 {
1096 if (isCOMinterface() || isCPPinterface())
1097 return 0;
1098 return 1;
1099 }
1100
isCPPinterface()1101 override bool isCPPinterface() const
1102 {
1103 return classKind == ClassKind.cpp;
1104 }
1105
isCOMinterface()1106 override bool isCOMinterface() const
1107 {
1108 return com;
1109 }
1110
inout(InterfaceDeclaration)1111 override inout(InterfaceDeclaration) isInterfaceDeclaration() inout
1112 {
1113 return this;
1114 }
1115
accept(Visitor v)1116 override void accept(Visitor v)
1117 {
1118 v.visit(this);
1119 }
1120 }
1121
1122 /**
1123 * Returns whether `bc` implements `id`, including indirectly (`bc` implements an interfaces
1124 * that inherits from `id`)
1125 *
1126 * Params:
1127 * id = the interface
1128 * bc = the base class
1129 * poffset = out parameter, offset of the interface in an object
1130 *
1131 * Returns:
1132 * true if the `bc` implements `id`, false otherwise
1133 **/
baseClassImplementsInterface(InterfaceDeclaration id,BaseClass * bc,int * poffset)1134 private bool baseClassImplementsInterface(InterfaceDeclaration id, BaseClass* bc, int* poffset) pure nothrow @nogc
1135 {
1136 //printf("%s.InterfaceDeclaration.isBaseOf(bc = '%s')\n", id.toChars(), bc.sym.toChars());
1137 for (size_t j = 0; j < bc.baseInterfaces.length; j++)
1138 {
1139 BaseClass* b = &bc.baseInterfaces[j];
1140 //printf("\tY base %s\n", b.sym.toChars());
1141 if (id == b.sym)
1142 {
1143 //printf("\tfound at offset %d\n", b.offset);
1144 if (poffset)
1145 {
1146 *poffset = b.offset;
1147 }
1148 return true;
1149 }
1150 if (baseClassImplementsInterface(id, b, poffset))
1151 {
1152 return true;
1153 }
1154 }
1155
1156 if (poffset)
1157 *poffset = 0;
1158 return false;
1159 }
1160