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 }