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 }