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