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