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