1 /**
2  * Interfacing with Objective-C.
3  *
4  * Specification: $(LINK2 https://dlang.org/spec/objc_interface.html, Interfacing to Objective-C)
5  *
6  * Copyright:   Copyright (C) 1999-2023 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/objc.d, _objc.d)
10  * Documentation:  https://dlang.org/phobos/dmd_objc.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/objc.d
12  */
13 
14 module dmd.objc;
15 
16 import dmd.aggregate;
17 import dmd.arraytypes;
18 import dmd.astenums;
19 import dmd.attrib;
20 import dmd.cond;
21 import dmd.dclass;
22 import dmd.declaration;
23 import dmd.denum;
24 import dmd.dmangle;
25 import dmd.dmodule;
26 import dmd.dscope;
27 import dmd.dstruct;
28 import dmd.dsymbol;
29 import dmd.dsymbolsem;
30 import dmd.errors;
31 import dmd.expression;
32 import dmd.expressionsem;
33 import dmd.func;
34 import dmd.globals;
35 import dmd.gluelayer;
36 import dmd.hdrgen;
37 import dmd.id;
38 import dmd.identifier;
39 import dmd.location;
40 import dmd.mtype;
41 import dmd.root.array;
42 import dmd.common.outbuffer;
43 import dmd.root.stringtable;
44 import dmd.target;
45 import dmd.tokens;
46 
47 struct ObjcSelector
48 {
49     // MARK: Selector
50     private __gshared StringTable!(ObjcSelector*) stringtable;
51     private __gshared int incnum = 0;
52     const(char)* stringvalue;
53     size_t stringlen;
54     size_t paramCount;
55 
56     extern (C++) static void _init()
57     {
58         stringtable._init();
59     }
60 
61     extern (D) this(const(char)* sv, size_t len, size_t pcount) @safe
62     {
63         stringvalue = sv;
64         stringlen = len;
65         paramCount = pcount;
66     }
67 
68     extern (D) static ObjcSelector* lookup(const(char)* s)
69     {
70         size_t len = 0;
71         size_t pcount = 0;
72         const(char)* i = s;
73         while (*i != 0)
74         {
75             ++len;
76             if (*i == ':')
77                 ++pcount;
78             ++i;
79         }
80         return lookup(s, len, pcount);
81     }
82 
83     extern (D) static ObjcSelector* lookup(const(char)* s, size_t len, size_t pcount)
84     {
85         auto sv = stringtable.update(s, len);
86         ObjcSelector* sel = sv.value;
87         if (!sel)
88         {
89             sel = new ObjcSelector(sv.toDchars(), len, pcount);
90             sv.value = sel;
91         }
92         return sel;
93     }
94 
95     extern (C++) static ObjcSelector* create(FuncDeclaration fdecl)
96     {
97         OutBuffer buf;
98         TypeFunction ftype = cast(TypeFunction)fdecl.type;
99         const id = fdecl.ident.toString();
100         const nparams = ftype.parameterList.length;
101         // Special case: property setter
102         if (ftype.isproperty && nparams == 1)
103         {
104             // rewrite "identifier" as "setIdentifier"
105             char firstChar = id[0];
106             if (firstChar >= 'a' && firstChar <= 'z')
107                 firstChar = cast(char)(firstChar - 'a' + 'A');
108             buf.writestring("set");
109             buf.writeByte(firstChar);
110             buf.write(id[1 .. id.length - 1]);
111             buf.writeByte(':');
112             goto Lcomplete;
113         }
114         // write identifier in selector
115         buf.write(id[]);
116         // add mangled type and colon for each parameter
117         if (nparams)
118         {
119             buf.writeByte('_');
120             foreach (i, fparam; ftype.parameterList)
121             {
122                 mangleToBuffer(fparam.type, buf);
123                 buf.writeByte(':');
124             }
125         }
126     Lcomplete:
127         buf.writeByte('\0');
128         // the slice is not expected to include a terminating 0
129         return lookup(cast(const(char)*)buf[].ptr, buf.length - 1, nparams);
130     }
131 
132     extern (D) const(char)[] toString() const pure
133     {
134         return stringvalue[0 .. stringlen];
135     }
136 }
137 
138 private __gshared Objc _objc;
139 
140 Objc objc()
141 {
142     return _objc;
143 }
144 
145 
146 /**
147  * Contains all data for a class declaration that is needed for the Objective-C
148  * integration.
149  */
150 extern (C++) struct ObjcClassDeclaration
151 {
152     /// `true` if this class is a metaclass.
153     bool isMeta = false;
154 
155     /// `true` if this class is externally defined.
156     bool isExtern = false;
157 
158     /// Name of this class.
159     Identifier identifier;
160 
161     /// The class declaration this belongs to.
162     ClassDeclaration classDeclaration;
163 
164     /// The metaclass of this class.
165     ClassDeclaration metaclass;
166 
167     /// List of non-inherited methods.
168     FuncDeclaration[] methodList;
169 
170     extern (D) this(ClassDeclaration classDeclaration) @safe
171     {
172         this.classDeclaration = classDeclaration;
173     }
174 
175     bool isRootClass() const @safe
176     {
177         return classDeclaration.classKind == ClassKind.objc &&
178             !metaclass &&
179             !classDeclaration.baseClass;
180     }
181 }
182 
183 /**
184  * Contains all data for a function declaration that is needed for the
185  * Objective-C integration.
186  */
187 extern (C++) struct ObjcFuncDeclaration
188 {
189     /// The method selector (member functions only).
190     ObjcSelector* selector;
191 
192     /// The implicit selector parameter.
193     VarDeclaration selectorParameter;
194 
195     /// `true` if this function declaration is declared optional.
196     bool isOptional;
197 }
198 
199 // Should be an interface
200 extern(C++) abstract class Objc
201 {
202     static void _init()
203     {
204         if (target.objc.supported)
205             _objc = new Supported;
206         else
207             _objc = new Unsupported;
208     }
209 
210     /**
211      * Deinitializes the global state of the compiler.
212      *
213      * This can be used to restore the state set by `_init` to its original
214      * state.
215      */
216     static void deinitialize()
217     {
218         _objc = _objc.init;
219     }
220 
221     abstract void setObjc(ClassDeclaration cd);
222     abstract void setObjc(InterfaceDeclaration);
223 
224     /**
225      * Returns a pretty textual representation of the given class declaration.
226      *
227      * Params:
228      *  classDeclaration = the class declaration to return the textual representation for
229      *  qualifyTypes = `true` if types should be qualified in the result
230      *
231      * Returns: the textual representation
232      */
233     abstract const(char)* toPrettyChars(ClassDeclaration classDeclaration, bool qualifyTypes) const;
234 
235     abstract void setSelector(FuncDeclaration, Scope* sc);
236     abstract void validateSelector(FuncDeclaration fd);
237     abstract void checkLinkage(FuncDeclaration fd);
238 
239     /**
240      * Returns `true` if the given function declaration is virtual.
241      *
242      * Function declarations with Objective-C linkage and which are static or
243      * final are considered virtual.
244      *
245      * Params:
246      *  fd = the function declaration to check if it's virtual
247      *
248      * Returns: `true` if the given function declaration is virtual
249      */
250     abstract bool isVirtual(const FuncDeclaration fd) const;
251 
252     /**
253      * Marks the given function declaration as optional.
254      *
255      * A function declaration is considered optional if it's annotated with the
256      * UDA: `@(core.attribute.optional)`. Only function declarations inside
257      * interface declarations and with Objective-C linkage can be declared as
258      * optional.
259      *
260      * Params:
261      *  functionDeclaration = the function declaration to be set as optional
262      *  sc = the scope from the semantic phase
263      */
264     abstract void setAsOptional(FuncDeclaration functionDeclaration, Scope* sc) const;
265 
266     /**
267      * Validates function declarations declared optional.
268      *
269      * Params:
270      *  functionDeclaration = the function declaration to validate
271      */
272     abstract void validateOptional(FuncDeclaration functionDeclaration) const;
273 
274     /**
275      * Gets the parent of the given function declaration.
276      *
277      * Handles Objective-C static member functions, which are virtual functions
278      * of the metaclass, by returning the parent class declaration to the
279      * metaclass.
280      *
281      * Params:
282      *  fd = the function declaration to get the parent of
283      *  cd = the current parent, i.e. the class declaration the given function
284      *      declaration belongs to
285      *
286      * Returns: the parent
287      */
288     abstract ClassDeclaration getParent(FuncDeclaration fd,
289         ClassDeclaration cd) const;
290 
291     /**
292      * Adds the given function to the list of Objective-C methods.
293      *
294      * This list will later be used output the necessary Objective-C module info.
295      *
296      * Params:
297      *  fd = the function declaration to be added to the list
298      *  cd = the class declaration the function belongs to
299      */
300     abstract void addToClassMethodList(FuncDeclaration fd,
301         ClassDeclaration cd) const;
302 
303     /**
304      * Returns the `this` pointer of the given function declaration.
305      *
306      * This is only used for class/static methods. For instance methods, no
307      * Objective-C specialization is necessary.
308      *
309      * Params:
310      *  funcDeclaration = the function declaration to get the `this` pointer for
311      *
312      * Returns: the `this` pointer of the given function declaration, or `null`
313      *  if the given function declaration is not an Objective-C method.
314      */
315     abstract inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const;
316 
317     /**
318      * Creates the selector parameter for the given function declaration.
319      *
320      * Objective-C methods has an extra hidden parameter that comes after the
321      * `this` parameter. The selector parameter is of the Objective-C type `SEL`
322      * and contains the selector which this method was called with.
323      *
324      * Params:
325      *  fd = the function declaration to create the parameter for
326      *  sc = the scope from the semantic phase
327      *
328      * Returns: the newly created selector parameter or `null` for
329      *  non-Objective-C functions
330      */
331     abstract VarDeclaration createSelectorParameter(FuncDeclaration fd, Scope* sc) const;
332 
333     /**
334      * Creates and sets the metaclass on the given class/interface declaration.
335      *
336      * Will only be performed on regular Objective-C classes, not on metaclasses.
337      *
338      * Params:
339      *  classDeclaration = the class/interface declaration to set the metaclass on
340      */
341     abstract void setMetaclass(InterfaceDeclaration interfaceDeclaration, Scope* sc) const;
342 
343     /// ditto
344     abstract void setMetaclass(ClassDeclaration classDeclaration, Scope* sc) const;
345 
346     /**
347      * Returns Objective-C runtime metaclass of the given class declaration.
348      *
349      * `ClassDeclaration.ObjcClassDeclaration.metaclass` contains the metaclass
350      * from the semantic point of view. This function returns the metaclass from
351      * the Objective-C runtime's point of view. Here, the metaclass of a
352      * metaclass is the root metaclass, not `null`. The root metaclass's
353      * metaclass is itself.
354      *
355      * Params:
356      *  classDeclaration = The class declaration to return the metaclass of
357      *
358      * Returns: the Objective-C runtime metaclass of the given class declaration
359      */
360     abstract ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const;
361 
362     ///
363     abstract void addSymbols(AttribDeclaration attribDeclaration,
364         ClassDeclarations* classes, ClassDeclarations* categories) const;
365 
366     ///
367     abstract void addSymbols(ClassDeclaration classDeclaration,
368         ClassDeclarations* classes, ClassDeclarations* categories) const;
369 
370     /**
371      * Issues a compile time error if the `.offsetof`/`.tupleof` property is
372      * used on a field of an Objective-C class.
373      *
374      * To solve the fragile base class problem in Objective-C, fields have a
375      * dynamic offset instead of a static offset. The compiler outputs a
376      * statically known offset which later the dynamic loader can update, if
377      * necessary, when the application is loaded. Due to this behavior it
378      * doesn't make sense to be able to get the offset of a field at compile
379      * time, because this offset might not actually be the same at runtime.
380      *
381      * To get the offset of a field that is correct at runtime, functionality
382      * from the Objective-C runtime can be used instead.
383      *
384      * Params:
385      *  expression = the `.offsetof`/`.tupleof` expression
386      *  aggregateDeclaration = the aggregate declaration the field of the
387      *      `.offsetof`/`.tupleof` expression belongs to
388      *  type = the type of the receiver of the `.tupleof` expression
389      *
390      * See_Also:
391      *  $(LINK2 https://en.wikipedia.org/wiki/Fragile_binary_interface_problem,
392      *      Fragile Binary Interface Problem)
393      *
394      * See_Also:
395      *  $(LINK2 https://developer.apple.com/documentation/objectivec/objective_c_runtime,
396      *      Objective-C Runtime)
397      */
398     abstract void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const;
399 
400     /// ditto
401     abstract void checkTupleof(Expression expression, TypeClass type) const;
402 }
403 
404 extern(C++) private final class Unsupported : Objc
405 {
406     extern(D) final this()
407     {
408         ObjcGlue.initialize();
409     }
410 
411     override void setObjc(ClassDeclaration cd)
412     {
413         .error(cd.loc, "%s `%s` Objective-C classes not supported", cd.kind, cd.toPrettyChars);
414     }
415 
416     override void setObjc(InterfaceDeclaration id)
417     {
418         .error(id.loc, "%s `%s` Objective-C interfaces not supported", id.kind, id.toPrettyChars);
419     }
420 
421     override const(char)* toPrettyChars(ClassDeclaration, bool qualifyTypes) const
422     {
423         assert(0, "Should never be called when Objective-C is not supported");
424     }
425 
426     override void setSelector(FuncDeclaration, Scope*)
427     {
428         // noop
429     }
430 
431     override void validateSelector(FuncDeclaration)
432     {
433         // noop
434     }
435 
436     override void checkLinkage(FuncDeclaration)
437     {
438         // noop
439     }
440 
441     override bool isVirtual(const FuncDeclaration) const
442     {
443         assert(0, "Should never be called when Objective-C is not supported");
444     }
445 
446     override void setAsOptional(FuncDeclaration, Scope*) const
447     {
448         // noop
449     }
450 
451     override void validateOptional(FuncDeclaration) const
452     {
453         // noop
454     }
455 
456     override ClassDeclaration getParent(FuncDeclaration, ClassDeclaration cd) const
457     {
458         return cd;
459     }
460 
461     override void addToClassMethodList(FuncDeclaration, ClassDeclaration) const
462     {
463         // noop
464     }
465 
466     override inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const
467     {
468         return null;
469     }
470 
471     override VarDeclaration createSelectorParameter(FuncDeclaration, Scope*) const
472     {
473         return null;
474     }
475 
476     override void setMetaclass(InterfaceDeclaration, Scope*) const
477     {
478         // noop
479     }
480 
481     override void setMetaclass(ClassDeclaration, Scope*) const
482     {
483         // noop
484     }
485 
486     override ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const
487     {
488         assert(0, "Should never be called when Objective-C is not supported");
489     }
490 
491     override void addSymbols(AttribDeclaration attribDeclaration,
492         ClassDeclarations* classes, ClassDeclarations* categories) const
493     {
494         // noop
495     }
496 
497     override void addSymbols(ClassDeclaration classDeclaration,
498         ClassDeclarations* classes, ClassDeclarations* categories) const
499     {
500         // noop
501     }
502 
503     override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const
504     {
505         // noop
506     }
507 
508     override void checkTupleof(Expression expression, TypeClass type) const
509     {
510         // noop
511     }
512 }
513 
514 extern(C++) private final class Supported : Objc
515 {
516     extern(D) final this()
517     {
518         VersionCondition.addPredefinedGlobalIdent("D_ObjectiveC");
519 
520         ObjcGlue.initialize();
521         ObjcSelector._init();
522     }
523 
524     override void setObjc(ClassDeclaration cd)
525     {
526         cd.classKind = ClassKind.objc;
527         cd.objc.isExtern = (cd.storage_class & STC.extern_) > 0;
528     }
529 
530     override void setObjc(InterfaceDeclaration id)
531     {
532         id.classKind = ClassKind.objc;
533         id.objc.isExtern = true;
534     }
535 
536     override const(char)* toPrettyChars(ClassDeclaration cd, bool qualifyTypes) const
537     {
538         return cd.parent.toPrettyChars(qualifyTypes);
539     }
540 
541     override void setSelector(FuncDeclaration fd, Scope* sc)
542     {
543         foreachUda(fd, sc, (e) {
544             if (!e.isStructLiteralExp())
545                 return 0;
546 
547             auto literal = e.isStructLiteralExp();
548             assert(literal.sd);
549 
550             if (!isCoreUda(literal.sd, Id.udaSelector))
551                 return 0;
552 
553             if (fd.objc.selector)
554             {
555                 .error(fd.loc, "%s `%s` can only have one Objective-C selector per method", fd.kind, fd.toPrettyChars);
556                 return 1;
557             }
558 
559             assert(literal.elements.length == 1);
560             auto se = (*literal.elements)[0].toStringExp();
561             assert(se);
562 
563             fd.objc.selector = ObjcSelector.lookup(se.toUTF8(sc).peekString().ptr);
564 
565             return 0;
566         });
567     }
568 
569     override void validateSelector(FuncDeclaration fd)
570     {
571         if (!fd.objc.selector)
572             return;
573         TypeFunction tf = cast(TypeFunction)fd.type;
574         if (fd.objc.selector.paramCount != tf.parameterList.parameters.length)
575             .error(fd.loc, "%s `%s` number of colons in Objective-C selector must match number of parameters", fd.kind, fd.toPrettyChars);
576         if (fd.parent && fd.parent.isTemplateInstance())
577             .error(fd.loc, "%s `%s` template cannot have an Objective-C selector attached", fd.kind, fd.toPrettyChars);
578     }
579 
580     override void checkLinkage(FuncDeclaration fd)
581     {
582         if (fd._linkage != LINK.objc && fd.objc.selector)
583             .error(fd.loc, "%s `%s` must have Objective-C linkage to attach a selector", fd.kind, fd.toPrettyChars);
584     }
585 
586     override bool isVirtual(const FuncDeclaration fd) const
587     in
588     {
589         assert(fd.selector);
590         assert(fd.isMember);
591     }
592     do
593     {
594         if (fd.toParent.isInterfaceDeclaration && fd.isFinal)
595             return false;
596 
597         // * final member functions are kept virtual with Objective-C linkage
598         //   because the Objective-C runtime always use dynamic dispatch.
599         // * static member functions are kept virtual too, as they represent
600         //   methods of the metaclass.
601         with (fd.visibility)
602             return !(kind == Visibility.Kind.private_ || kind == Visibility.Kind.package_);
603     }
604 
605     override void setAsOptional(FuncDeclaration fd, Scope* sc) const
606     {
607         const count = declaredAsOptionalCount(fd, sc);
608         fd.objc.isOptional = count > 0;
609 
610         if (count > 1)
611             .error(fd.loc, "%s `%s` can only declare a function as optional once", fd.kind, fd.toPrettyChars);
612     }
613 
614     /// Returns: the number of times `fd` has been declared as optional.
615     private int declaredAsOptionalCount(FuncDeclaration fd , Scope* sc) const
616     {
617         int count;
618 
619         foreachUda(fd, sc, (e) {
620             if (!e.isTypeExp())
621                 return 0;
622 
623             auto typeExp = e.isTypeExp();
624 
625             if (typeExp.type.ty != Tenum)
626                 return 0;
627 
628             auto typeEnum = cast(TypeEnum) typeExp.type;
629 
630             if (isCoreUda(typeEnum.sym, Id.udaOptional))
631                 count++;
632 
633             return 0;
634         });
635 
636         return count;
637     }
638 
639     override void validateOptional(FuncDeclaration fd) const
640     {
641         if (!fd.objc.isOptional)
642             return;
643 
644         if (fd._linkage != LINK.objc)
645         {
646             .error(fd.loc, "%s `%s` only functions with Objective-C linkage can be declared as optional", fd.kind, fd.toPrettyChars);
647 
648             const linkage = linkageToString(fd._linkage);
649 
650             errorSupplemental(fd.loc, "function is declared with %.*s linkage",
651                 cast(uint) linkage.length, linkage.ptr);
652         }
653 
654         auto parent = fd.parent;
655 
656         if (parent && parent.isTemplateInstance())
657         {
658             .error(fd.loc, "%s `%s` template cannot be optional", fd.kind, fd.toPrettyChars);
659             parent = parent.parent;
660             assert(parent);
661         }
662 
663         if (parent && !parent.isInterfaceDeclaration())
664         {
665             .error(fd.loc, "%s `%s` only functions declared inside interfaces can be optional", fd.kind, fd.toPrettyChars);
666             errorSupplemental(fd.loc, "function is declared inside %s", fd.parent.kind);
667         }
668     }
669 
670     override ClassDeclaration getParent(FuncDeclaration fd, ClassDeclaration cd) const
671     out(metaclass)
672     {
673         assert(metaclass);
674     }
675     do
676     {
677         if (cd.classKind == ClassKind.objc && fd.isStatic && !cd.objc.isMeta)
678             return cd.objc.metaclass;
679         else
680             return cd;
681     }
682 
683     override void addToClassMethodList(FuncDeclaration fd, ClassDeclaration cd) const
684     in
685     {
686         assert(fd.parent.isClassDeclaration);
687     }
688     do
689     {
690         if (cd.classKind != ClassKind.objc)
691             return;
692 
693         if (!fd.objc.selector)
694             return;
695 
696         assert(fd.isStatic ? cd.objc.isMeta : !cd.objc.isMeta);
697 
698         cd.objc.methodList ~= fd;
699     }
700 
701     override inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const
702     {
703         with(funcDeclaration)
704         {
705             if (!objc.selector)
706                 return null;
707 
708             // Use Objective-C class object as 'this'
709             auto cd = isMember2().isClassDeclaration();
710 
711             if (cd.classKind == ClassKind.objc)
712             {
713                 if (!cd.objc.isMeta)
714                     return cd.objc.metaclass;
715             }
716 
717             return null;
718         }
719     }
720 
721     override VarDeclaration createSelectorParameter(FuncDeclaration fd, Scope* sc) const
722     in
723     {
724         assert(fd.selectorParameter is null);
725     }
726     do
727     {
728         if (!fd.objc.selector)
729             return null;
730 
731         auto ident = Identifier.generateAnonymousId("_cmd");
732         auto var = new VarDeclaration(fd.loc, Type.tvoidptr, ident, null);
733         var.storage_class |= STC.parameter;
734         var.dsymbolSemantic(sc);
735         if (!sc.insert(var))
736             assert(false);
737         var.parent = fd;
738 
739         return var;
740     }
741 
742     override void setMetaclass(InterfaceDeclaration interfaceDeclaration, Scope* sc) const
743     {
744         auto newMetaclass(Loc loc, BaseClasses* metaBases)
745         {
746             auto ident = createMetaclassIdentifier(interfaceDeclaration);
747             return new InterfaceDeclaration(loc, ident, metaBases);
748         }
749 
750         .setMetaclass!newMetaclass(interfaceDeclaration, sc);
751     }
752 
753     override void setMetaclass(ClassDeclaration classDeclaration, Scope* sc) const
754     {
755         auto newMetaclass(Loc loc, BaseClasses* metaBases)
756         {
757             auto ident = createMetaclassIdentifier(classDeclaration);
758             return new ClassDeclaration(loc, ident, metaBases, new Dsymbols(), 0);
759         }
760 
761         .setMetaclass!newMetaclass(classDeclaration, sc);
762     }
763 
764     override ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const
765     {
766         if (!classDeclaration.objc.metaclass && classDeclaration.objc.isMeta)
767         {
768             if (classDeclaration.baseClass)
769                 return getRuntimeMetaclass(classDeclaration.baseClass);
770             else
771                 return classDeclaration;
772         }
773         else
774             return classDeclaration.objc.metaclass;
775     }
776 
777     override void addSymbols(AttribDeclaration attribDeclaration,
778         ClassDeclarations* classes, ClassDeclarations* categories) const
779     {
780         auto symbols = attribDeclaration.include(null);
781 
782         if (!symbols)
783             return;
784 
785         foreach (symbol; *symbols)
786             symbol.addObjcSymbols(classes, categories);
787     }
788 
789     override void addSymbols(ClassDeclaration classDeclaration,
790         ClassDeclarations* classes, ClassDeclarations* categories) const
791     {
792         with (classDeclaration)
793             if (classKind == ClassKind.objc && !objc.isExtern && !objc.isMeta)
794                 classes.push(classDeclaration);
795     }
796 
797     override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const
798     {
799         if (aggregateDeclaration.classKind != ClassKind.objc)
800             return;
801 
802         enum errorMessage = "no property `offsetof` for member `%s` of type " ~
803             "`%s`";
804 
805         enum supplementalMessage = "`offsetof` is not available for members " ~
806             "of Objective-C classes. Please use the Objective-C runtime instead";
807 
808         error(expression.loc, errorMessage, expression.toChars(),
809             expression.type.toChars());
810         errorSupplemental(expression.loc, supplementalMessage);
811     }
812 
813     override void checkTupleof(Expression expression, TypeClass type) const
814     {
815         if (type.sym.classKind != ClassKind.objc)
816             return;
817 
818         error(expression.loc, "no property `tupleof` for type `%s`", type.toChars());
819         errorSupplemental(expression.loc, "`tupleof` is not available for members " ~
820             "of Objective-C classes. Please use the Objective-C runtime instead");
821     }
822 }
823 
824 /*
825  * Creates and sets the metaclass on the given class/interface declaration.
826  *
827  * Will only be performed on regular Objective-C classes, not on metaclasses.
828  *
829  * Params:
830  *  newMetaclass = a function that returns the metaclass to set. This should
831  *      return the same type as `T`.
832  *  classDeclaration = the class/interface declaration to set the metaclass on
833  */
834 private void setMetaclass(alias newMetaclass, T)(T classDeclaration, Scope* sc)
835 if (is(T == ClassDeclaration) || is(T == InterfaceDeclaration))
836 {
837     static if (is(T == ClassDeclaration))
838         enum errorType = "class";
839     else
840         enum errorType = "interface";
841 
842     with (classDeclaration)
843     {
844         if (classKind != ClassKind.objc || objc.isMeta || objc.metaclass)
845             return;
846 
847         if (!objc.identifier)
848             objc.identifier = classDeclaration.ident;
849 
850         auto metaBases = new BaseClasses();
851 
852         foreach (base ; baseclasses.opSlice)
853         {
854             auto baseCd = base.sym;
855             assert(baseCd);
856 
857             if (baseCd.classKind == ClassKind.objc)
858             {
859                 assert(baseCd.objc.metaclass);
860                 assert(baseCd.objc.metaclass.objc.isMeta);
861                 assert(baseCd.objc.metaclass.type.ty == Tclass);
862 
863                 auto metaBase = new BaseClass(baseCd.objc.metaclass.type);
864                 metaBase.sym = baseCd.objc.metaclass;
865                 metaBases.push(metaBase);
866             }
867             else
868             {
869                 .error(classDeclaration.loc, "%s `%s` base " ~ errorType ~ " for an Objective-C " ~
870                       errorType ~ " must be `extern (Objective-C)`", classDeclaration.kind, classDeclaration.toPrettyChars);
871             }
872         }
873 
874         objc.metaclass = newMetaclass(loc, metaBases);
875         objc.metaclass.storage_class |= STC.static_;
876         objc.metaclass.classKind = ClassKind.objc;
877         objc.metaclass.objc.isMeta = true;
878         objc.metaclass.objc.isExtern = objc.isExtern;
879         objc.metaclass.objc.identifier = objc.identifier;
880 
881         if (baseClass)
882             objc.metaclass.baseClass = baseClass.objc.metaclass;
883 
884         members.push(objc.metaclass);
885         objc.metaclass.addMember(sc, classDeclaration);
886 
887         objc.metaclass.members = new Dsymbols();
888         objc.metaclass.dsymbolSemantic(sc);
889     }
890 }
891 
892 private Identifier createMetaclassIdentifier(ClassDeclaration classDeclaration)
893 {
894     const name = "class_" ~ classDeclaration.ident.toString ~ "_Meta";
895     return Identifier.generateAnonymousId(name);
896 }