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