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