1 /**
2  * Generate `TypeInfo` objects, which are needed for run-time introspection of classes.
3  *
4  * Copyright:   Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/typeinf.d, _typeinf.d)
8  * Documentation:  https://dlang.org/phobos/dmd_typinf.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/typinf.d
10  */
11 
12 module dmd.typinf;
13 
14 import dmd.declaration;
15 import dmd.dmodule;
16 import dmd.dscope;
17 import dmd.dclass;
18 import dmd.dstruct;
19 import dmd.errors;
20 import dmd.globals;
21 import dmd.gluelayer;
22 import dmd.mtype;
23 import dmd.visitor;
24 import core.stdc.stdio;
25 
26 /****************************************************
27  * Generates the `TypeInfo` object associated with `torig` if it
28  * hasn't already been generated
29  * Params:
30  *      loc   = the location for reporting line numbers in errors
31  *      torig = the type to generate the `TypeInfo` object for
32  *      sc    = the scope
33  */
34 void genTypeInfo(Loc loc, Type torig, Scope* sc)
35 {
36     // printf("genTypeInfo() %s\n", torig.toChars());
37 
38     // Even when compiling without `useTypeInfo` (e.g. -betterC) we should
39     // still be able to evaluate `TypeInfo` at compile-time, just not at runtime.
40     // https://issues.dlang.org/show_bug.cgi?id=18472
41     if (!sc || !(sc.flags & SCOPE.ctfe))
42     {
43         if (!global.params.useTypeInfo)
44         {
45             .error(loc, "`TypeInfo` cannot be used with -betterC");
46             fatal();
47         }
48     }
49 
50     if (!Type.dtypeinfo)
51     {
52         .error(loc, "`object.TypeInfo` could not be found, but is implicitly used");
53         fatal();
54     }
55 
56     Type t = torig.merge2(); // do this since not all Type's are merge'd
57     if (!t.vtinfo)
58     {
59         if (t.isShared()) // does both 'shared' and 'shared const'
60             t.vtinfo = TypeInfoSharedDeclaration.create(t);
61         else if (t.isConst())
62             t.vtinfo = TypeInfoConstDeclaration.create(t);
63         else if (t.isImmutable())
64             t.vtinfo = TypeInfoInvariantDeclaration.create(t);
65         else if (t.isWild())
66             t.vtinfo = TypeInfoWildDeclaration.create(t);
67         else
68             t.vtinfo = getTypeInfoDeclaration(t);
69         assert(t.vtinfo);
70 
71         /* If this has a custom implementation in std/typeinfo, then
72          * do not generate a COMDAT for it.
73          */
74         if (!builtinTypeInfo(t))
75         {
76             // Generate COMDAT
77             if (sc) // if in semantic() pass
78             {
79                 // Find module that will go all the way to an object file
80                 Module m = sc._module.importedFrom;
81                 m.members.push(t.vtinfo);
82             }
83             else // if in obj generation pass
84             {
85                 toObjFile(t.vtinfo, global.params.multiobj);
86             }
87         }
88     }
89     if (!torig.vtinfo)
90         torig.vtinfo = t.vtinfo; // Types aren't merged, but we can share the vtinfo's
91     assert(torig.vtinfo);
92 }
93 
94 /****************************************************
95  * Gets the type of the `TypeInfo` object associated with `t`
96  * Params:
97  *      loc = the location for reporting line nunbers in errors
98  *      t   = the type to get the type of the `TypeInfo` object for
99  *      sc  = the scope
100  * Returns:
101  *      The type of the `TypeInfo` object associated with `t`
102  */
103 extern (C++) Type getTypeInfoType(Loc loc, Type t, Scope* sc)
104 {
105     assert(t.ty != Terror);
106     genTypeInfo(loc, t, sc);
107     return t.vtinfo.type;
108 }
109 
110 private TypeInfoDeclaration getTypeInfoDeclaration(Type t)
111 {
112     //printf("Type::getTypeInfoDeclaration() %s\n", t.toChars());
113     switch (t.ty)
114     {
115     case Tpointer:
116         return TypeInfoPointerDeclaration.create(t);
117     case Tarray:
118         return TypeInfoArrayDeclaration.create(t);
119     case Tsarray:
120         return TypeInfoStaticArrayDeclaration.create(t);
121     case Taarray:
122         return TypeInfoAssociativeArrayDeclaration.create(t);
123     case Tstruct:
124         return TypeInfoStructDeclaration.create(t);
125     case Tvector:
126         return TypeInfoVectorDeclaration.create(t);
127     case Tenum:
128         return TypeInfoEnumDeclaration.create(t);
129     case Tfunction:
130         return TypeInfoFunctionDeclaration.create(t);
131     case Tdelegate:
132         return TypeInfoDelegateDeclaration.create(t);
133     case Ttuple:
134         return TypeInfoTupleDeclaration.create(t);
135     case Tclass:
136         if ((cast(TypeClass)t).sym.isInterfaceDeclaration())
137             return TypeInfoInterfaceDeclaration.create(t);
138         else
139             return TypeInfoClassDeclaration.create(t);
140 
141     default:
142         return TypeInfoDeclaration.create(t);
143     }
144 }
145 
146 /**************************************************
147  * Returns:
148  *      true if any part of type t is speculative.
149  *      if t is null, returns false.
150  */
151 bool isSpeculativeType(Type t)
152 {
153     static bool visitVector(TypeVector t)
154     {
155         return isSpeculativeType(t.basetype);
156     }
157 
158     static bool visitAArray(TypeAArray t)
159     {
160         return isSpeculativeType(t.index) ||
161                isSpeculativeType(t.next);
162     }
163 
164     static bool visitStruct(TypeStruct t)
165     {
166         StructDeclaration sd = t.sym;
167         if (auto ti = sd.isInstantiated())
168         {
169             if (!ti.needsCodegen())
170             {
171                 if (ti.minst || sd.requestTypeInfo)
172                     return false;
173 
174                 /* https://issues.dlang.org/show_bug.cgi?id=14425
175                  * TypeInfo_Struct would refer the members of
176                  * struct (e.g. opEquals via xopEquals field), so if it's instantiated
177                  * in speculative context, TypeInfo creation should also be
178                  * stopped to avoid 'unresolved symbol' linker errors.
179                  */
180                 /* When -debug/-unittest is specified, all of non-root instances are
181                  * automatically changed to speculative, and here is always reached
182                  * from those instantiated non-root structs.
183                  * Therefore, if the TypeInfo is not auctually requested,
184                  * we have to elide its codegen.
185                  */
186                 return true;
187             }
188         }
189         else
190         {
191             //assert(!sd.inNonRoot() || sd.requestTypeInfo);    // valid?
192         }
193         return false;
194     }
195 
196     static bool visitClass(TypeClass t)
197     {
198         ClassDeclaration sd = t.sym;
199         if (auto ti = sd.isInstantiated())
200         {
201             if (!ti.needsCodegen() && !ti.minst)
202             {
203                 return true;
204             }
205         }
206         return false;
207     }
208 
209 
210     static bool visitTuple(TypeTuple t)
211     {
212         if (t.arguments)
213         {
214             foreach (arg; *t.arguments)
215             {
216                 if (isSpeculativeType(arg.type))
217                     return true;
218             }
219         }
220         return false;
221     }
222 
223     if (!t)
224         return false;
225     Type tb = t.toBasetype();
226     switch (tb.ty)
227     {
228         case Tvector:   return visitVector(tb.isTypeVector());
229         case Taarray:   return visitAArray(tb.isTypeAArray());
230         case Tstruct:   return visitStruct(tb.isTypeStruct());
231         case Tclass:    return visitClass(tb.isTypeClass());
232         case Ttuple:    return visitTuple(tb.isTypeTuple());
233         case Tenum:     return false;
234         default:
235         return isSpeculativeType(tb.nextOf());
236 
237         /* For TypeFunction, TypeInfo_Function doesn't store parameter types,
238          * so only the .next (the return type) is checked here.
239          */
240     }
241 }
242 
243 /* ========================================================================= */
244 
245 /* These decide if there's an instance for them already in std.typeinfo,
246  * because then the compiler doesn't need to build one.
247  */
248 private bool builtinTypeInfo(Type t)
249 {
250     if (t.isTypeBasic() || t.ty == Tclass || t.ty == Tnull)
251         return !t.mod;
252     if (t.ty == Tarray)
253     {
254         Type next = t.nextOf();
255         // strings are so common, make them builtin
256         return !t.mod &&
257                (next.isTypeBasic() !is null && !next.mod ||
258                 next.ty == Tchar && next.mod == MODFlags.immutable_ ||
259                 next.ty == Tchar && next.mod == MODFlags.const_);
260     }
261     return false;
262 }