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