| /** |
| * Generate `TypeInfo` objects, which are needed for run-time introspection of types. |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/typeinf.d, _typeinf.d) |
| * Documentation: https://dlang.org/phobos/dmd_typinf.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/typinf.d |
| */ |
| |
| module dmd.typinf; |
| |
| import dmd.astenums; |
| import dmd.declaration; |
| import dmd.dmodule; |
| import dmd.dscope; |
| import dmd.dclass; |
| import dmd.dstruct; |
| import dmd.errors; |
| import dmd.expression; |
| import dmd.globals; |
| import dmd.gluelayer; |
| import dmd.mtype; |
| import dmd.visitor; |
| import core.stdc.stdio; |
| |
| /**************************************************** |
| * Generates the `TypeInfo` object associated with `torig` if it |
| * hasn't already been generated |
| * Params: |
| * e = if not null, then expression for pretty-printing errors |
| * loc = the location for reporting line numbers in errors |
| * torig = the type to generate the `TypeInfo` object for |
| * sc = the scope |
| */ |
| extern (C++) void genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) |
| { |
| // printf("genTypeInfo() %s\n", torig.toChars()); |
| |
| // Even when compiling without `useTypeInfo` (e.g. -betterC) we should |
| // still be able to evaluate `TypeInfo` at compile-time, just not at runtime. |
| // https://issues.dlang.org/show_bug.cgi?id=18472 |
| if (!sc || !(sc.flags & SCOPE.ctfe)) |
| { |
| if (!global.params.useTypeInfo) |
| { |
| if (e) |
| .error(loc, "expression `%s` uses the GC and cannot be used with switch `-betterC`", e.toChars()); |
| else |
| .error(loc, "`TypeInfo` cannot be used with -betterC"); |
| fatal(); |
| } |
| } |
| |
| if (!Type.dtypeinfo) |
| { |
| .error(loc, "`object.TypeInfo` could not be found, but is implicitly used"); |
| fatal(); |
| } |
| |
| Type t = torig.merge2(); // do this since not all Type's are merge'd |
| if (!t.vtinfo) |
| { |
| if (t.isShared()) // does both 'shared' and 'shared const' |
| t.vtinfo = TypeInfoSharedDeclaration.create(t); |
| else if (t.isConst()) |
| t.vtinfo = TypeInfoConstDeclaration.create(t); |
| else if (t.isImmutable()) |
| t.vtinfo = TypeInfoInvariantDeclaration.create(t); |
| else if (t.isWild()) |
| t.vtinfo = TypeInfoWildDeclaration.create(t); |
| else |
| t.vtinfo = getTypeInfoDeclaration(t); |
| assert(t.vtinfo); |
| |
| // ClassInfos are generated as part of ClassDeclaration codegen |
| const isUnqualifiedClassInfo = (t.ty == Tclass && !t.mod); |
| |
| // generate a COMDAT for other TypeInfos not available as builtins in |
| // druntime |
| if (!isUnqualifiedClassInfo && !builtinTypeInfo(t)) |
| { |
| if (sc) // if in semantic() pass |
| { |
| // Find module that will go all the way to an object file |
| Module m = sc._module.importedFrom; |
| m.members.push(t.vtinfo); |
| } |
| else // if in obj generation pass |
| { |
| toObjFile(t.vtinfo, global.params.multiobj); |
| } |
| } |
| } |
| if (!torig.vtinfo) |
| torig.vtinfo = t.vtinfo; // Types aren't merged, but we can share the vtinfo's |
| assert(torig.vtinfo); |
| } |
| |
| /**************************************************** |
| * Gets the type of the `TypeInfo` object associated with `t` |
| * Params: |
| * loc = the location for reporting line nunbers in errors |
| * t = the type to get the type of the `TypeInfo` object for |
| * sc = the scope |
| * Returns: |
| * The type of the `TypeInfo` object associated with `t` |
| */ |
| extern (C++) Type getTypeInfoType(const ref Loc loc, Type t, Scope* sc); |
| |
| private TypeInfoDeclaration getTypeInfoDeclaration(Type t) |
| { |
| //printf("Type::getTypeInfoDeclaration() %s\n", t.toChars()); |
| switch (t.ty) |
| { |
| case Tpointer: |
| return TypeInfoPointerDeclaration.create(t); |
| case Tarray: |
| return TypeInfoArrayDeclaration.create(t); |
| case Tsarray: |
| return TypeInfoStaticArrayDeclaration.create(t); |
| case Taarray: |
| return TypeInfoAssociativeArrayDeclaration.create(t); |
| case Tstruct: |
| return TypeInfoStructDeclaration.create(t); |
| case Tvector: |
| return TypeInfoVectorDeclaration.create(t); |
| case Tenum: |
| return TypeInfoEnumDeclaration.create(t); |
| case Tfunction: |
| return TypeInfoFunctionDeclaration.create(t); |
| case Tdelegate: |
| return TypeInfoDelegateDeclaration.create(t); |
| case Ttuple: |
| return TypeInfoTupleDeclaration.create(t); |
| case Tclass: |
| if ((cast(TypeClass)t).sym.isInterfaceDeclaration()) |
| return TypeInfoInterfaceDeclaration.create(t); |
| else |
| return TypeInfoClassDeclaration.create(t); |
| |
| default: |
| return TypeInfoDeclaration.create(t); |
| } |
| } |
| |
| /************************************************** |
| * Returns: |
| * true if any part of type t is speculative. |
| * if t is null, returns false. |
| */ |
| bool isSpeculativeType(Type t) |
| { |
| static bool visitVector(TypeVector t) |
| { |
| return isSpeculativeType(t.basetype); |
| } |
| |
| static bool visitAArray(TypeAArray t) |
| { |
| return isSpeculativeType(t.index) || |
| isSpeculativeType(t.next); |
| } |
| |
| static bool visitStruct(TypeStruct t) |
| { |
| StructDeclaration sd = t.sym; |
| if (auto ti = sd.isInstantiated()) |
| { |
| if (!ti.needsCodegen()) |
| { |
| if (ti.minst || sd.requestTypeInfo) |
| return false; |
| |
| /* https://issues.dlang.org/show_bug.cgi?id=14425 |
| * TypeInfo_Struct would refer the members of |
| * struct (e.g. opEquals via xopEquals field), so if it's instantiated |
| * in speculative context, TypeInfo creation should also be |
| * stopped to avoid 'unresolved symbol' linker errors. |
| */ |
| /* When -debug/-unittest is specified, all of non-root instances are |
| * automatically changed to speculative, and here is always reached |
| * from those instantiated non-root structs. |
| * Therefore, if the TypeInfo is not auctually requested, |
| * we have to elide its codegen. |
| */ |
| return true; |
| } |
| } |
| else |
| { |
| //assert(!sd.inNonRoot() || sd.requestTypeInfo); // valid? |
| } |
| return false; |
| } |
| |
| static bool visitClass(TypeClass t) |
| { |
| ClassDeclaration sd = t.sym; |
| if (auto ti = sd.isInstantiated()) |
| { |
| if (!ti.needsCodegen() && !ti.minst) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| static bool visitTuple(TypeTuple t) |
| { |
| if (t.arguments) |
| { |
| foreach (arg; *t.arguments) |
| { |
| if (isSpeculativeType(arg.type)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| if (!t) |
| return false; |
| Type tb = t.toBasetype(); |
| switch (tb.ty) |
| { |
| case Tvector: return visitVector(tb.isTypeVector()); |
| case Taarray: return visitAArray(tb.isTypeAArray()); |
| case Tstruct: return visitStruct(tb.isTypeStruct()); |
| case Tclass: return visitClass(tb.isTypeClass()); |
| case Ttuple: return visitTuple(tb.isTypeTuple()); |
| case Tenum: return false; |
| default: |
| return isSpeculativeType(tb.nextOf()); |
| |
| /* For TypeFunction, TypeInfo_Function doesn't store parameter types, |
| * so only the .next (the return type) is checked here. |
| */ |
| } |
| } |
| |
| /* ========================================================================= */ |
| |
| /* Indicates whether druntime already contains an appropriate TypeInfo instance |
| * for the specified type (in module rt.util.typeinfo). |
| */ |
| extern (C++) bool builtinTypeInfo(Type t) |
| { |
| if (!t.mod) // unqualified types only |
| { |
| // unqualified basic types + typeof(null) |
| if (t.isTypeBasic() || t.ty == Tnull) |
| return true; |
| // some unqualified arrays |
| if (t.ty == Tarray) |
| { |
| Type next = t.nextOf(); |
| return (next.isTypeBasic() && !next.mod) // of unqualified basic types |
| || (next.ty == Tchar && next.mod == MODFlags.immutable_) // string |
| || (next.ty == Tchar && next.mod == MODFlags.const_); // const(char)[] |
| } |
| } |
| return false; |
| } |