| //===--- TypeLowering.cpp - Swift Type Lowering for Reflection ------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Implements logic for computing in-memory layouts from TypeRefs loaded from |
| // reflection metadata. |
| // |
| // This has to match up with layout algorithms used in IRGen and the runtime, |
| // and a bit of SIL type lowering to boot. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "swift/Reflection/TypeLowering.h" |
| #include "swift/Reflection/TypeRef.h" |
| #include "swift/Reflection/TypeRefBuilder.h" |
| #include "swift/Runtime/Unreachable.h" |
| |
| #include <iostream> |
| |
| #ifdef DEBUG_TYPE_LOWERING |
| #define DEBUG(expr) expr; |
| #else |
| #define DEBUG(expr) |
| #endif |
| |
| namespace swift { |
| namespace reflection { |
| |
| void TypeInfo::dump() const { |
| dump(std::cerr); |
| } |
| |
| namespace { |
| |
| class PrintTypeInfo { |
| std::ostream &OS; |
| unsigned Indent; |
| |
| std::ostream &indent(unsigned Amount) { |
| for (unsigned i = 0; i < Amount; ++i) |
| OS << ' '; |
| return OS; |
| } |
| |
| std::ostream &printHeader(const std::string &name) { |
| indent(Indent) << '(' << name; |
| return OS; |
| } |
| |
| template<typename T> |
| std::ostream &printField(const std::string &name, const T &value) { |
| if (!name.empty()) |
| OS << " " << name << "=" << value; |
| else |
| OS << " " << value; |
| return OS; |
| } |
| |
| void printRec(const TypeInfo &TI) { |
| OS << "\n"; |
| |
| Indent += 2; |
| print(TI); |
| Indent -= 2; |
| } |
| |
| void printBasic(const TypeInfo &TI) { |
| printField("size", TI.getSize()); |
| printField("alignment", TI.getAlignment()); |
| printField("stride", TI.getStride()); |
| printField("num_extra_inhabitants", TI.getNumExtraInhabitants()); |
| } |
| |
| void printFields(const RecordTypeInfo &TI) { |
| Indent += 2; |
| for (auto Field : TI.getFields()) { |
| OS << "\n"; |
| printHeader("field"); |
| if (!Field.Name.empty()) |
| printField("name", Field.Name); |
| printField("offset", Field.Offset); |
| printRec(Field.TI); |
| OS << ")"; |
| } |
| Indent -= 2; |
| } |
| |
| public: |
| PrintTypeInfo(std::ostream &OS, unsigned Indent) |
| : OS(OS), Indent(Indent) {} |
| |
| void print(const TypeInfo &TI) { |
| switch (TI.getKind()) { |
| case TypeInfoKind::Builtin: |
| printHeader("builtin"); |
| printBasic(TI); |
| OS << ")"; |
| return; |
| |
| case TypeInfoKind::Record: { |
| auto &RecordTI = cast<RecordTypeInfo>(TI); |
| switch (RecordTI.getRecordKind()) { |
| case RecordKind::Invalid: |
| printHeader("invalid"); |
| break; |
| case RecordKind::Struct: |
| printHeader("struct"); |
| break; |
| case RecordKind::NoPayloadEnum: |
| printHeader("no_payload_enum"); |
| break; |
| case RecordKind::SinglePayloadEnum: |
| printHeader("single_payload_enum"); |
| break; |
| case RecordKind::MultiPayloadEnum: |
| printHeader("multi_payload_enum"); |
| break; |
| case RecordKind::Tuple: |
| printHeader("tuple"); |
| break; |
| case RecordKind::ThickFunction: |
| printHeader("thick_function"); |
| break; |
| case RecordKind::OpaqueExistential: |
| printHeader("opaque_existential"); |
| break; |
| case RecordKind::ClassExistential: |
| printHeader("class_existential"); |
| break; |
| case RecordKind::ErrorExistential: |
| printHeader("error_existential"); |
| break; |
| case RecordKind::ExistentialMetatype: |
| printHeader("existential_metatype"); |
| break; |
| case RecordKind::ClassInstance: |
| printHeader("class_instance"); |
| break; |
| case RecordKind::ClosureContext: |
| printHeader("closure_context"); |
| break; |
| } |
| printBasic(TI); |
| printFields(RecordTI); |
| OS << ")"; |
| return; |
| } |
| |
| case TypeInfoKind::Reference: { |
| printHeader("reference"); |
| auto &ReferenceTI = cast<ReferenceTypeInfo>(TI); |
| switch (ReferenceTI.getReferenceKind()) { |
| case ReferenceKind::Strong: |
| printField("kind", "strong"); |
| break; |
| case ReferenceKind::Unowned: |
| printField("kind", "unowned"); |
| break; |
| case ReferenceKind::Weak: |
| printField("kind", "weak"); |
| break; |
| case ReferenceKind::Unmanaged: |
| printField("kind", "unmanaged"); |
| break; |
| } |
| |
| switch (ReferenceTI.getReferenceCounting()) { |
| case ReferenceCounting::Native: |
| printField("refcounting", "native"); |
| break; |
| case ReferenceCounting::Unknown: |
| printField("refcounting", "unknown"); |
| break; |
| } |
| |
| OS << ")"; |
| return; |
| } |
| } |
| |
| swift_runtime_unreachable("Bad TypeInfo kind"); |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| void TypeInfo::dump(std::ostream &OS, unsigned Indent) const { |
| PrintTypeInfo(OS, Indent).print(*this); |
| OS << '\n'; |
| } |
| |
| BuiltinTypeInfo::BuiltinTypeInfo(const BuiltinTypeDescriptor *descriptor) |
| : TypeInfo(TypeInfoKind::Builtin, |
| descriptor->Size, descriptor->Alignment, |
| descriptor->Stride, descriptor->NumExtraInhabitants), |
| Name(descriptor->getMangledTypeName()) {} |
| |
| /// Utility class for building values that contain witness tables. |
| class ExistentialTypeInfoBuilder { |
| TypeConverter &TC; |
| std::vector<const ProtocolTypeRef *> Protocols; |
| ExistentialTypeRepresentation Representation; |
| ReferenceCounting Refcounting; |
| bool ObjC; |
| unsigned WitnessTableCount; |
| bool Invalid; |
| |
| bool isSingleError() const { |
| // If we changed representation, it means we added a |
| // superclass constraint or an AnyObject member. |
| if (Representation != ExistentialTypeRepresentation::Opaque) |
| return false; |
| |
| if (Protocols.size() != 1) |
| return false; |
| |
| for (auto *P : Protocols) { |
| if (P->isError()) |
| return true; |
| } |
| return false; |
| } |
| |
| void examineProtocols() { |
| if (isSingleError()) { |
| Representation = ExistentialTypeRepresentation::Error; |
| // No extra witness table for protocol<Error> |
| return; |
| } |
| |
| for (auto *P : Protocols) { |
| const FieldDescriptor *FD = TC.getBuilder().getFieldTypeInfo(P); |
| if (FD == nullptr) { |
| DEBUG(std::cerr << "No field descriptor: "; P->dump()) |
| Invalid = true; |
| continue; |
| } |
| |
| switch (FD->Kind) { |
| case FieldDescriptorKind::ObjCProtocol: |
| // Objective-C protocols do not have any witness tables. |
| ObjC = true; |
| continue; |
| case FieldDescriptorKind::ClassProtocol: |
| Representation = ExistentialTypeRepresentation::Class; |
| WitnessTableCount++; |
| continue; |
| case FieldDescriptorKind::Protocol: |
| WitnessTableCount++; |
| continue; |
| case FieldDescriptorKind::ObjCClass: |
| case FieldDescriptorKind::Struct: |
| case FieldDescriptorKind::Enum: |
| case FieldDescriptorKind::MultiPayloadEnum: |
| case FieldDescriptorKind::Class: |
| Invalid = true; |
| continue; |
| } |
| } |
| } |
| |
| public: |
| ExistentialTypeInfoBuilder(TypeConverter &TC) |
| : TC(TC), Representation(ExistentialTypeRepresentation::Opaque), |
| Refcounting(ReferenceCounting::Unknown), |
| ObjC(false), WitnessTableCount(0), |
| Invalid(false) {} |
| |
| void addProtocol(const ProtocolTypeRef *P) { |
| Protocols.push_back(P); |
| } |
| |
| void addProtocolComposition(const ProtocolCompositionTypeRef *PC) { |
| for (auto *T : PC->getMembers()) { |
| if (auto *P = dyn_cast<ProtocolTypeRef>(T)) { |
| addProtocol(P); |
| continue; |
| } |
| |
| if (auto *PC = dyn_cast<ProtocolCompositionTypeRef>(T)) { |
| addProtocolComposition(PC); |
| continue; |
| } |
| |
| // Anything else should either be a superclass constraint, or |
| // we have an invalid typeref. |
| if (!isa<NominalTypeRef>(T) && |
| !isa<BoundGenericTypeRef>(T)) { |
| DEBUG(std::cerr << "Bad existential member: "; T->dump()) |
| Invalid = true; |
| continue; |
| } |
| auto *FD = TC.getBuilder().getFieldTypeInfo(T); |
| if (FD == nullptr) { |
| DEBUG(std::cerr << "No field descriptor: "; T->dump()) |
| Invalid = true; |
| continue; |
| } |
| |
| // We have a valid superclass constraint. It only affects |
| // lowering by class-constraining the entire existential. |
| switch (FD->Kind) { |
| case FieldDescriptorKind::Class: |
| Refcounting = ReferenceCounting::Native; |
| LLVM_FALLTHROUGH; |
| |
| case FieldDescriptorKind::ObjCClass: |
| addAnyObject(); |
| break; |
| |
| default: |
| DEBUG(std::cerr << "Bad existential member: "; T->dump()) |
| Invalid = true; |
| continue; |
| } |
| } |
| |
| if (PC->hasExplicitAnyObject()) |
| addAnyObject(); |
| } |
| |
| void addAnyObject() { |
| Representation = ExistentialTypeRepresentation::Class; |
| } |
| |
| void markInvalid() { |
| Invalid = true; |
| } |
| |
| const TypeInfo *build() { |
| examineProtocols(); |
| |
| if (Invalid) |
| return nullptr; |
| |
| if (ObjC) { |
| if (WitnessTableCount > 0) { |
| DEBUG(std::cerr << "@objc existential with witness tables\n"); |
| return nullptr; |
| } |
| |
| return TC.getReferenceTypeInfo(ReferenceKind::Strong, |
| Refcounting); |
| } |
| |
| RecordKind Kind; |
| switch (Representation) { |
| case ExistentialTypeRepresentation::Class: |
| Kind = RecordKind::ClassExistential; |
| break; |
| case ExistentialTypeRepresentation::Opaque: |
| Kind = RecordKind::OpaqueExistential; |
| break; |
| case ExistentialTypeRepresentation::Error: |
| Kind = RecordKind::ErrorExistential; |
| break; |
| } |
| |
| RecordTypeInfoBuilder builder(TC, Kind); |
| |
| switch (Representation) { |
| case ExistentialTypeRepresentation::Class: |
| // Class existentials consist of a single retainable pointer |
| // followed by witness tables. |
| if (Refcounting == ReferenceCounting::Unknown) |
| builder.addField("object", TC.getUnknownObjectTypeRef()); |
| else |
| builder.addField("object", TC.getNativeObjectTypeRef()); |
| break; |
| case ExistentialTypeRepresentation::Opaque: { |
| auto *TI = TC.getTypeInfo(TC.getRawPointerTypeRef()); |
| if (TI == nullptr) { |
| DEBUG(std::cerr << "No TypeInfo for RawPointer\n"); |
| return nullptr; |
| } |
| |
| // Non-class existentials consist of a three-word buffer, |
| // value metadata, and finally zero or more witness tables. |
| builder.addField(TI->getSize() * 3, |
| TI->getAlignment(), |
| /*numExtraInhabitants=*/0); |
| builder.addField("metadata", TC.getAnyMetatypeTypeRef()); |
| break; |
| } |
| case ExistentialTypeRepresentation::Error: |
| builder.addField("error", TC.getUnknownObjectTypeRef()); |
| break; |
| } |
| |
| for (unsigned i = 0; i < WitnessTableCount; i++) |
| builder.addField("wtable", TC.getRawPointerTypeRef()); |
| |
| return builder.build(); |
| } |
| |
| const TypeInfo *buildMetatype() { |
| examineProtocols(); |
| |
| if (Invalid) |
| return nullptr; |
| |
| if (ObjC) { |
| if (WitnessTableCount > 0) { |
| DEBUG(std::cerr << "@objc existential with witness tables\n"); |
| return nullptr; |
| } |
| |
| return TC.getAnyMetatypeTypeInfo(); |
| } |
| |
| RecordTypeInfoBuilder builder(TC, RecordKind::ExistentialMetatype); |
| |
| builder.addField("metadata", TC.getAnyMetatypeTypeRef()); |
| for (unsigned i = 0; i < WitnessTableCount; i++) |
| builder.addField("wtable", TC.getRawPointerTypeRef()); |
| |
| return builder.build(); |
| } |
| }; |
| |
| unsigned RecordTypeInfoBuilder::addField(unsigned fieldSize, |
| unsigned fieldAlignment, |
| unsigned numExtraInhabitants) { |
| assert(fieldAlignment > 0); |
| |
| // Align the current size appropriately |
| Size = ((Size + fieldAlignment - 1) & ~(fieldAlignment - 1)); |
| |
| // Record the offset |
| unsigned offset = Size; |
| |
| // Update the aggregate size |
| Size += fieldSize; |
| |
| // Update the aggregate alignment |
| Alignment = std::max(Alignment, fieldAlignment); |
| |
| // The extra inhabitants of a record are the same as the extra |
| // inhabitants of the first field of the record. |
| if (Empty) { |
| NumExtraInhabitants = numExtraInhabitants; |
| Empty = false; |
| } |
| |
| return offset; |
| } |
| |
| void RecordTypeInfoBuilder::addField(const std::string &Name, |
| const TypeRef *TR) { |
| const TypeInfo *TI = TC.getTypeInfo(TR); |
| if (TI == nullptr) { |
| DEBUG(std::cerr << "No TypeInfo for field type: "; TR->dump()); |
| Invalid = true; |
| return; |
| } |
| |
| unsigned offset = addField(TI->getSize(), |
| TI->getAlignment(), |
| TI->getNumExtraInhabitants()); |
| Fields.push_back({Name, offset, TR, *TI}); |
| } |
| |
| const RecordTypeInfo *RecordTypeInfoBuilder::build() { |
| if (Invalid) |
| return nullptr; |
| |
| // Calculate the stride |
| unsigned Stride = ((Size + Alignment - 1) & ~(Alignment - 1)); |
| if (Stride == 0) |
| Stride = 1; |
| |
| return TC.makeTypeInfo<RecordTypeInfo>( |
| Size, Alignment, Stride, |
| NumExtraInhabitants, Kind, Fields); |
| } |
| |
| const ReferenceTypeInfo * |
| TypeConverter::getReferenceTypeInfo(ReferenceKind Kind, |
| ReferenceCounting Refcounting) { |
| auto key = std::make_pair(unsigned(Kind), unsigned(Refcounting)); |
| auto found = ReferenceCache.find(key); |
| if (found != ReferenceCache.end()) |
| return found->second; |
| |
| const TypeRef *TR; |
| switch (Refcounting) { |
| case ReferenceCounting::Native: |
| TR = getNativeObjectTypeRef(); |
| break; |
| case ReferenceCounting::Unknown: |
| TR = getUnknownObjectTypeRef(); |
| break; |
| } |
| |
| // Unowned and unmanaged references have the same extra inhabitants |
| // as the underlying type. |
| // |
| // Weak references do not have any extra inhabitants. |
| |
| auto *BuiltinTI = Builder.getBuiltinTypeInfo(TR); |
| if (BuiltinTI == nullptr) { |
| DEBUG(std::cerr << "No TypeInfo for reference type: "; TR->dump()); |
| return nullptr; |
| } |
| |
| unsigned numExtraInhabitants = BuiltinTI->NumExtraInhabitants; |
| if (Kind == ReferenceKind::Weak) |
| numExtraInhabitants = 0; |
| |
| auto *TI = makeTypeInfo<ReferenceTypeInfo>(BuiltinTI->Size, |
| BuiltinTI->Alignment, |
| BuiltinTI->Stride, |
| numExtraInhabitants, |
| Kind, Refcounting); |
| ReferenceCache[key] = TI; |
| return TI; |
| } |
| |
| /// Thick functions consist of a function pointer. We do not use |
| /// Builtin.RawPointer here, since the extra inhabitants differ. |
| const TypeInfo * |
| TypeConverter::getThinFunctionTypeInfo() { |
| if (ThinFunctionTI != nullptr) |
| return ThinFunctionTI; |
| |
| auto *descriptor = getBuilder().getBuiltinTypeInfo( |
| getThinFunctionTypeRef()); |
| if (descriptor == nullptr) { |
| DEBUG(std::cerr << "No TypeInfo for function type\n"); |
| return nullptr; |
| } |
| |
| ThinFunctionTI = makeTypeInfo<BuiltinTypeInfo>(descriptor); |
| |
| return ThinFunctionTI; |
| } |
| |
| /// Thick functions consist of a function pointer and nullable retainable |
| /// context pointer. The context is modeled exactly like a native Swift |
| /// class reference. |
| const TypeInfo * |
| TypeConverter::getThickFunctionTypeInfo() { |
| if (ThickFunctionTI != nullptr) |
| return ThickFunctionTI; |
| |
| RecordTypeInfoBuilder builder(*this, RecordKind::ThickFunction); |
| builder.addField("function", getThinFunctionTypeRef()); |
| builder.addField("context", getNativeObjectTypeRef()); |
| ThickFunctionTI = builder.build(); |
| |
| return ThickFunctionTI; |
| } |
| |
| /// Thick metatypes consist of a single pointer, possibly followed |
| /// by witness tables. We do not use Builtin.RawPointer here, since |
| /// the extra inhabitants differ. |
| const TypeInfo * |
| TypeConverter::getAnyMetatypeTypeInfo() { |
| if (AnyMetatypeTI != nullptr) |
| return AnyMetatypeTI; |
| |
| auto *descriptor = getBuilder().getBuiltinTypeInfo( |
| getAnyMetatypeTypeRef()); |
| if (descriptor == nullptr) { |
| DEBUG(std::cerr << "No TypeInfo for metatype type\n"); |
| return nullptr; |
| } |
| |
| AnyMetatypeTI = makeTypeInfo<BuiltinTypeInfo>(descriptor); |
| |
| return AnyMetatypeTI; |
| } |
| |
| const TypeInfo *TypeConverter::getEmptyTypeInfo() { |
| if (EmptyTI != nullptr) |
| return EmptyTI; |
| |
| EmptyTI = makeTypeInfo<TypeInfo>(TypeInfoKind::Builtin, 0, 1, 1, 0); |
| return EmptyTI; |
| } |
| |
| const TypeRef *TypeConverter::getRawPointerTypeRef() { |
| if (RawPointerTR != nullptr) |
| return RawPointerTR; |
| |
| RawPointerTR = BuiltinTypeRef::create(Builder, "Bp"); |
| return RawPointerTR; |
| } |
| |
| const TypeRef *TypeConverter::getNativeObjectTypeRef() { |
| if (NativeObjectTR != nullptr) |
| return NativeObjectTR; |
| |
| NativeObjectTR = BuiltinTypeRef::create(Builder, "Bo"); |
| return NativeObjectTR; |
| } |
| |
| const TypeRef *TypeConverter::getUnknownObjectTypeRef() { |
| if (UnknownObjectTR != nullptr) |
| return UnknownObjectTR; |
| |
| UnknownObjectTR = BuiltinTypeRef::create(Builder, "BO"); |
| return UnknownObjectTR; |
| } |
| |
| const TypeRef *TypeConverter::getThinFunctionTypeRef() { |
| if (ThinFunctionTR != nullptr) |
| return ThinFunctionTR; |
| |
| ThinFunctionTR = BuiltinTypeRef::create(Builder, "yyXf"); |
| return ThinFunctionTR; |
| } |
| |
| const TypeRef *TypeConverter::getAnyMetatypeTypeRef() { |
| if (AnyMetatypeTR != nullptr) |
| return AnyMetatypeTR; |
| |
| AnyMetatypeTR = BuiltinTypeRef::create(Builder, "ypXp"); |
| return AnyMetatypeTR; |
| } |
| |
| enum class MetatypeRepresentation : unsigned { |
| /// Singleton metatype values are empty. |
| Thin, |
| |
| /// Metatypes containing classes, or where the original unsubstituted |
| /// type contains a type parameter, must be represented as pointers |
| /// to metadata structures. |
| Thick, |
| |
| /// Insufficient information to determine which. |
| Unknown |
| }; |
| |
| /// Visitor class to determine if a type has a fixed size. |
| /// |
| /// Conservative approximation. |
| class HasFixedSize |
| : public TypeRefVisitor<HasFixedSize, bool> { |
| |
| public: |
| HasFixedSize() {} |
| |
| using TypeRefVisitor<HasFixedSize, bool>::visit; |
| |
| bool visitBuiltinTypeRef(const BuiltinTypeRef *B) { |
| return true; |
| } |
| |
| bool visitNominalTypeRef(const NominalTypeRef *N) { |
| return true; |
| } |
| |
| bool visitBoundGenericTypeRef(const BoundGenericTypeRef *BG) { |
| if (BG->isClass()) |
| return true; |
| for (auto Arg : BG->getGenericParams()) { |
| if (!visit(Arg)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool visitTupleTypeRef(const TupleTypeRef *T) { |
| for (auto Element : T->getElements()) |
| if (!visit(Element)) |
| return false; |
| return true; |
| } |
| |
| bool visitFunctionTypeRef(const FunctionTypeRef *F) { |
| return true; |
| } |
| |
| bool visitProtocolTypeRef(const ProtocolTypeRef *P) { |
| return true; |
| } |
| |
| bool |
| visitProtocolCompositionTypeRef(const ProtocolCompositionTypeRef *PC) { |
| return true; |
| } |
| |
| bool visitMetatypeTypeRef(const MetatypeTypeRef *M) { |
| return true; |
| } |
| |
| bool |
| visitExistentialMetatypeTypeRef(const ExistentialMetatypeTypeRef *EM) { |
| return true; |
| } |
| |
| bool |
| visitSILBoxTypeRef(const SILBoxTypeRef *SB) { |
| return true; |
| } |
| |
| bool |
| visitForeignClassTypeRef(const ForeignClassTypeRef *F) { |
| return true; |
| } |
| |
| bool visitObjCClassTypeRef(const ObjCClassTypeRef *OC) { |
| return true; |
| } |
| |
| bool |
| visitUnownedStorageTypeRef(const UnownedStorageTypeRef *US) { |
| return true; |
| } |
| |
| bool visitWeakStorageTypeRef(const WeakStorageTypeRef *WS) { |
| return true; |
| } |
| |
| bool |
| visitUnmanagedStorageTypeRef(const UnmanagedStorageTypeRef *US) { |
| return true; |
| } |
| |
| bool |
| visitGenericTypeParameterTypeRef(const GenericTypeParameterTypeRef *GTP) { |
| return false; |
| } |
| |
| bool |
| visitDependentMemberTypeRef(const DependentMemberTypeRef *DM) { |
| return false; |
| } |
| |
| bool visitOpaqueTypeRef(const OpaqueTypeRef *O) { |
| return false; |
| } |
| }; |
| |
| bool TypeConverter::hasFixedSize(const TypeRef *TR) { |
| return HasFixedSize().visit(TR); |
| } |
| |
| MetatypeRepresentation combineRepresentations(MetatypeRepresentation rep1, |
| MetatypeRepresentation rep2) { |
| if (rep1 == rep2) |
| return rep1; |
| |
| if (rep1 == MetatypeRepresentation::Unknown || |
| rep2 == MetatypeRepresentation::Unknown) |
| return MetatypeRepresentation::Unknown; |
| |
| if (rep1 == MetatypeRepresentation::Thick || |
| rep2 == MetatypeRepresentation::Thick) |
| return MetatypeRepresentation::Thick; |
| |
| return MetatypeRepresentation::Thin; |
| } |
| |
| /// Visitor class to determine if a metatype should use the empty |
| /// representation. |
| /// |
| /// This relies on substitution correctly setting wasAbstract() on |
| /// MetatypeTypeRefs. |
| class HasSingletonMetatype |
| : public TypeRefVisitor<HasSingletonMetatype, MetatypeRepresentation> { |
| |
| public: |
| HasSingletonMetatype() {} |
| |
| using TypeRefVisitor<HasSingletonMetatype, MetatypeRepresentation>::visit; |
| |
| MetatypeRepresentation visitBuiltinTypeRef(const BuiltinTypeRef *B) { |
| return MetatypeRepresentation::Thin; |
| } |
| |
| MetatypeRepresentation visitNominalTypeRef(const NominalTypeRef *N) { |
| if (N->isClass()) |
| return MetatypeRepresentation::Thick; |
| return MetatypeRepresentation::Thin; |
| } |
| |
| MetatypeRepresentation visitBoundGenericTypeRef(const BoundGenericTypeRef *BG) { |
| if (BG->isClass()) |
| return MetatypeRepresentation::Thick; |
| return MetatypeRepresentation::Thin; |
| } |
| |
| MetatypeRepresentation visitTupleTypeRef(const TupleTypeRef *T) { |
| auto result = MetatypeRepresentation::Thin; |
| for (auto Element : T->getElements()) |
| result = combineRepresentations(result, visit(Element)); |
| return result; |
| } |
| |
| MetatypeRepresentation visitFunctionTypeRef(const FunctionTypeRef *F) { |
| auto result = visit(F->getResult()); |
| for (auto Arg : F->getArguments()) |
| result = combineRepresentations(result, visit(Arg)); |
| return result; |
| } |
| |
| MetatypeRepresentation visitProtocolTypeRef(const ProtocolTypeRef *P) { |
| return MetatypeRepresentation::Thin; |
| } |
| |
| MetatypeRepresentation |
| visitProtocolCompositionTypeRef(const ProtocolCompositionTypeRef *PC) { |
| return MetatypeRepresentation::Thin; |
| } |
| |
| MetatypeRepresentation visitMetatypeTypeRef(const MetatypeTypeRef *M) { |
| if (M->wasAbstract()) |
| return MetatypeRepresentation::Thick; |
| return visit(M->getInstanceType()); |
| } |
| |
| MetatypeRepresentation |
| visitExistentialMetatypeTypeRef(const ExistentialMetatypeTypeRef *EM) { |
| return MetatypeRepresentation::Thin; |
| } |
| |
| MetatypeRepresentation |
| visitSILBoxTypeRef(const SILBoxTypeRef *SB) { |
| return MetatypeRepresentation::Thin; |
| } |
| |
| MetatypeRepresentation |
| visitGenericTypeParameterTypeRef(const GenericTypeParameterTypeRef *GTP) { |
| DEBUG(std::cerr << "Unresolved generic TypeRef: "; GTP->dump()); |
| return MetatypeRepresentation::Unknown; |
| } |
| |
| MetatypeRepresentation |
| visitDependentMemberTypeRef(const DependentMemberTypeRef *DM) { |
| DEBUG(std::cerr << "Unresolved generic TypeRef: "; DM->dump()); |
| return MetatypeRepresentation::Unknown; |
| } |
| |
| MetatypeRepresentation |
| visitForeignClassTypeRef(const ForeignClassTypeRef *F) { |
| return MetatypeRepresentation::Unknown; |
| } |
| |
| MetatypeRepresentation visitObjCClassTypeRef(const ObjCClassTypeRef *OC) { |
| return MetatypeRepresentation::Unknown; |
| } |
| |
| MetatypeRepresentation |
| visitUnownedStorageTypeRef(const UnownedStorageTypeRef *US) { |
| return MetatypeRepresentation::Unknown; |
| } |
| |
| MetatypeRepresentation visitWeakStorageTypeRef(const WeakStorageTypeRef *WS) { |
| return MetatypeRepresentation::Unknown; |
| } |
| |
| MetatypeRepresentation |
| visitUnmanagedStorageTypeRef(const UnmanagedStorageTypeRef *US) { |
| return MetatypeRepresentation::Unknown; |
| } |
| |
| MetatypeRepresentation visitOpaqueTypeRef(const OpaqueTypeRef *O) { |
| return MetatypeRepresentation::Unknown; |
| } |
| }; |
| |
| // Copy-and-pasted from stdlib/public/runtime/Enum.cpp -- should probably go |
| // in a header somewhere, since the formula is part of the ABI. |
| static unsigned getNumTagBytes(size_t size, unsigned emptyCases, |
| unsigned payloadCases) { |
| // We can use the payload area with a tag bit set somewhere outside of the |
| // payload area to represent cases. See how many bytes we need to cover |
| // all the empty cases. |
| |
| unsigned numTags = payloadCases; |
| if (emptyCases > 0) { |
| if (size >= 4) |
| // Assume that one tag bit is enough if the precise calculation overflows |
| // an int32. |
| numTags += 1; |
| else { |
| unsigned bits = size * 8U; |
| unsigned casesPerTagBitValue = 1U << bits; |
| numTags += ((emptyCases + (casesPerTagBitValue-1U)) >> bits); |
| } |
| } |
| return (numTags <= 1 ? 0 : |
| numTags < 256 ? 1 : |
| numTags < 65536 ? 2 : 4); |
| } |
| |
| class EnumTypeInfoBuilder { |
| TypeConverter &TC; |
| unsigned Size, Alignment, NumExtraInhabitants; |
| RecordKind Kind; |
| std::vector<FieldInfo> Cases; |
| bool Invalid; |
| |
| const TypeRef *getCaseTypeRef(FieldTypeInfo Case) { |
| // An indirect case is like a payload case with an argument type |
| // of Builtin.NativeObject. |
| if (Case.Indirect) |
| return TC.getNativeObjectTypeRef(); |
| |
| return Case.TR; |
| } |
| |
| void addCase(const std::string &Name, const TypeRef *TR, |
| const TypeInfo *TI) { |
| if (TI == nullptr) { |
| DEBUG(std::cerr << "No TypeInfo for case type: "; TR->dump()); |
| Invalid = true; |
| return; |
| } |
| |
| Size = std::max(Size, TI->getSize()); |
| Alignment = std::max(Alignment, TI->getAlignment()); |
| |
| Cases.push_back({Name, /*offset=*/0, TR, *TI}); |
| } |
| |
| public: |
| EnumTypeInfoBuilder(TypeConverter &TC) |
| : TC(TC), Size(0), Alignment(1), NumExtraInhabitants(0), |
| Kind(RecordKind::Invalid), Invalid(false) {} |
| |
| const TypeInfo *build(const TypeRef *TR, const FieldDescriptor *FD) { |
| // Sort enum into payload and no-payload cases. |
| unsigned NoPayloadCases = 0; |
| std::vector<FieldTypeInfo> PayloadCases; |
| |
| std::vector<FieldTypeInfo> Fields; |
| if (!TC.getBuilder().getFieldTypeRefs(TR, FD, Fields)) { |
| Invalid = true; |
| return nullptr; |
| } |
| |
| for (auto Case : Fields) { |
| if (Case.TR == nullptr) { |
| NoPayloadCases++; |
| continue; |
| } |
| |
| PayloadCases.push_back(Case); |
| } |
| |
| // NoPayloadEnumImplStrategy |
| if (PayloadCases.empty()) { |
| Kind = RecordKind::NoPayloadEnum; |
| Size += getNumTagBytes(/*size=*/0, |
| NoPayloadCases, |
| /*payloadCases=*/0); |
| |
| // SinglePayloadEnumImplStrategy |
| } else if (PayloadCases.size() == 1) { |
| auto *CaseTR = getCaseTypeRef(PayloadCases[0]); |
| auto *CaseTI = TC.getTypeInfo(CaseTR); |
| |
| // An enum consisting of a single payload case and nothing else |
| // is lowered as the payload type. |
| if (NoPayloadCases == 0) |
| return CaseTI; |
| |
| Kind = RecordKind::SinglePayloadEnum; |
| addCase(PayloadCases[0].Name, CaseTR, CaseTI); |
| |
| // If we were unable to lower the payload type, do not proceed |
| // further. |
| if (CaseTI != nullptr) { |
| // Below logic should match the runtime function |
| // swift_initEnumValueWitnessTableSinglePayload(). |
| NumExtraInhabitants = CaseTI->getNumExtraInhabitants(); |
| if (NumExtraInhabitants >= NoPayloadCases) { |
| // Extra inhabitants can encode all no-payload cases. |
| NumExtraInhabitants -= NoPayloadCases; |
| } else { |
| // Not enough extra inhabitants for all cases. We have to add an |
| // extra tag field. |
| NumExtraInhabitants = 0; |
| Size += getNumTagBytes(Size, |
| NoPayloadCases - NumExtraInhabitants, |
| /*payloadCases=*/1); |
| } |
| } |
| |
| // MultiPayloadEnumImplStrategy |
| } else { |
| Kind = RecordKind::MultiPayloadEnum; |
| |
| // Check if this is a dynamic or static multi-payload enum |
| for (auto Case : PayloadCases) { |
| auto *CaseTR = getCaseTypeRef(Case); |
| auto *CaseTI = TC.getTypeInfo(CaseTR); |
| addCase(Case.Name, CaseTR, CaseTI); |
| } |
| |
| // If we have a fixed descriptor for this type, it is a fixed-size |
| // multi-payload enum that possibly uses payload spare bits. |
| auto *FixedDescriptor = TC.getBuilder().getBuiltinTypeInfo(TR); |
| if (FixedDescriptor) { |
| Size = FixedDescriptor->Size; |
| Alignment = FixedDescriptor->Alignment; |
| NumExtraInhabitants = FixedDescriptor->NumExtraInhabitants; |
| } else { |
| // Dynamic multi-payload enums do not have extra inhabitants |
| NumExtraInhabitants = 0; |
| |
| // Dynamic multi-payload enums always use an extra tag to differentiate |
| // between cases |
| Size += getNumTagBytes(Size, |
| NoPayloadCases, |
| PayloadCases.size()); |
| } |
| } |
| |
| if (Invalid) |
| return nullptr; |
| |
| // Calculate the stride |
| unsigned Stride = ((Size + Alignment - 1) & ~(Alignment - 1)); |
| if (Stride == 0) |
| Stride = 1; |
| |
| return TC.makeTypeInfo<RecordTypeInfo>( |
| Size, Alignment, Stride, |
| NumExtraInhabitants, Kind, Cases); |
| } |
| }; |
| |
| class LowerType |
| : public TypeRefVisitor<LowerType, const TypeInfo *> { |
| TypeConverter &TC; |
| |
| public: |
| using TypeRefVisitor<LowerType, const TypeInfo *>::visit; |
| |
| LowerType(TypeConverter &TC) : TC(TC) {} |
| |
| const TypeInfo *visitBuiltinTypeRef(const BuiltinTypeRef *B) { |
| /// The context field of a thick function is a Builtin.NativeObject. |
| /// Since we want this to round-trip, lower these as reference |
| /// types. |
| if (B->getMangledName() == "Bo") { |
| return TC.getReferenceTypeInfo(ReferenceKind::Strong, |
| ReferenceCounting::Native); |
| } else if (B->getMangledName() == "BO") { |
| return TC.getReferenceTypeInfo(ReferenceKind::Strong, |
| ReferenceCounting::Unknown); |
| } |
| |
| /// Otherwise, get the fixed layout information from reflection |
| /// metadata. |
| auto *descriptor = TC.getBuilder().getBuiltinTypeInfo(B); |
| if (descriptor == nullptr) { |
| DEBUG(std::cerr << "No TypeInfo for builtin type: "; B->dump()); |
| return nullptr; |
| } |
| return TC.makeTypeInfo<BuiltinTypeInfo>(descriptor); |
| } |
| |
| const TypeInfo *visitAnyNominalTypeRef(const TypeRef *TR) { |
| auto *FD = TC.getBuilder().getFieldTypeInfo(TR); |
| if (FD == nullptr) { |
| // Maybe this type is opaque -- look for a builtin |
| // descriptor to see if we at least know its size |
| // and alignment. |
| if (auto ImportedTypeDescriptor = TC.getBuilder().getBuiltinTypeInfo(TR)) |
| return TC.makeTypeInfo<BuiltinTypeInfo>(ImportedTypeDescriptor); |
| |
| // Otherwise, we're out of luck. |
| DEBUG(std::cerr << "No TypeInfo for nominal type: "; TR->dump()); |
| return nullptr; |
| } |
| |
| switch (FD->Kind) { |
| case FieldDescriptorKind::Class: |
| // A value of class type is a single retainable pointer. |
| return TC.getReferenceTypeInfo(ReferenceKind::Strong, |
| ReferenceCounting::Native); |
| case FieldDescriptorKind::Struct: { |
| // Lower the struct's fields using substitutions from the |
| // TypeRef to make field types concrete. |
| RecordTypeInfoBuilder builder(TC, RecordKind::Struct); |
| |
| std::vector<FieldTypeInfo> Fields; |
| if (!TC.getBuilder().getFieldTypeRefs(TR, FD, Fields)) |
| return nullptr; |
| |
| for (auto Field : Fields) |
| builder.addField(Field.Name, Field.TR); |
| return builder.build(); |
| } |
| case FieldDescriptorKind::Enum: |
| case FieldDescriptorKind::MultiPayloadEnum: { |
| EnumTypeInfoBuilder builder(TC); |
| return builder.build(TR, FD); |
| } |
| case FieldDescriptorKind::ObjCClass: |
| return TC.getReferenceTypeInfo(ReferenceKind::Strong, |
| ReferenceCounting::Unknown); |
| case FieldDescriptorKind::ObjCProtocol: |
| case FieldDescriptorKind::ClassProtocol: |
| case FieldDescriptorKind::Protocol: |
| DEBUG(std::cerr << "Invalid field descriptor: "; TR->dump()); |
| return nullptr; |
| } |
| |
| swift_runtime_unreachable("Unhandled FieldDescriptorKind in switch."); |
| } |
| |
| const TypeInfo *visitNominalTypeRef(const NominalTypeRef *N) { |
| return visitAnyNominalTypeRef(N); |
| } |
| |
| const TypeInfo *visitBoundGenericTypeRef(const BoundGenericTypeRef *BG) { |
| return visitAnyNominalTypeRef(BG); |
| } |
| |
| const TypeInfo *visitTupleTypeRef(const TupleTypeRef *T) { |
| RecordTypeInfoBuilder builder(TC, RecordKind::Tuple); |
| for (auto Element : T->getElements()) |
| builder.addField("", Element); |
| return builder.build(); |
| } |
| |
| const TypeInfo *visitFunctionTypeRef(const FunctionTypeRef *F) { |
| switch (F->getFlags().getConvention()) { |
| case FunctionMetadataConvention::Swift: |
| return TC.getThickFunctionTypeInfo(); |
| case FunctionMetadataConvention::Block: |
| // FIXME: Native convention if blocks are ever supported on Linux? |
| return TC.getReferenceTypeInfo(ReferenceKind::Strong, |
| ReferenceCounting::Unknown); |
| case FunctionMetadataConvention::Thin: |
| case FunctionMetadataConvention::CFunctionPointer: |
| return TC.getTypeInfo(TC.getThinFunctionTypeRef()); |
| } |
| |
| swift_runtime_unreachable("Unhandled FunctionMetadataConvention in switch."); |
| } |
| |
| const TypeInfo *visitProtocolTypeRef(const ProtocolTypeRef *P) { |
| ExistentialTypeInfoBuilder builder(TC); |
| builder.addProtocol(P); |
| return builder.build(); |
| } |
| |
| const TypeInfo * |
| visitProtocolCompositionTypeRef(const ProtocolCompositionTypeRef *PC) { |
| ExistentialTypeInfoBuilder builder(TC); |
| builder.addProtocolComposition(PC); |
| return builder.build(); |
| } |
| |
| const TypeInfo *visitMetatypeTypeRef(const MetatypeTypeRef *M) { |
| switch (HasSingletonMetatype().visit(M)) { |
| case MetatypeRepresentation::Unknown: |
| DEBUG(std::cerr << "Unknown metatype representation: "; M->dump()); |
| return nullptr; |
| case MetatypeRepresentation::Thin: |
| return TC.getEmptyTypeInfo(); |
| case MetatypeRepresentation::Thick: |
| return TC.getTypeInfo(TC.getAnyMetatypeTypeRef()); |
| } |
| |
| swift_runtime_unreachable("Unhandled MetatypeRepresentation in switch."); |
| } |
| |
| const TypeInfo * |
| visitExistentialMetatypeTypeRef(const ExistentialMetatypeTypeRef *EM) { |
| ExistentialTypeInfoBuilder builder(TC); |
| auto *TR = EM->getInstanceType(); |
| |
| if (auto *P = dyn_cast<ProtocolTypeRef>(TR)) { |
| builder.addProtocol(P); |
| } else if (auto *PC = dyn_cast<ProtocolCompositionTypeRef>(TR)) { |
| builder.addProtocolComposition(PC); |
| } else { |
| DEBUG(std::cerr << "Invalid existential metatype: "; EM->dump()); |
| return nullptr; |
| } |
| |
| return builder.buildMetatype(); |
| } |
| |
| const TypeInfo * |
| visitGenericTypeParameterTypeRef(const GenericTypeParameterTypeRef *GTP) { |
| DEBUG(std::cerr << "Unresolved generic TypeRef: "; GTP->dump()); |
| return nullptr; |
| } |
| |
| const TypeInfo * |
| visitDependentMemberTypeRef(const DependentMemberTypeRef *DM) { |
| DEBUG(std::cerr << "Unresolved generic TypeRef: "; DM->dump()); |
| return nullptr; |
| } |
| |
| const TypeInfo *visitForeignClassTypeRef(const ForeignClassTypeRef *F) { |
| return TC.getReferenceTypeInfo(ReferenceKind::Strong, |
| ReferenceCounting::Unknown); |
| } |
| |
| const TypeInfo *visitObjCClassTypeRef(const ObjCClassTypeRef *OC) { |
| return TC.getReferenceTypeInfo(ReferenceKind::Strong, |
| ReferenceCounting::Unknown); |
| } |
| |
| // Apply a storage qualifier, like 'weak', 'unowned' or 'unowned(unsafe)' |
| // to a type with reference semantics, such as a class reference or |
| // class-bound existential. |
| const TypeInfo * |
| rebuildStorageTypeInfo(const TypeInfo *TI, ReferenceKind Kind) { |
| // If we can't lower the original storage type, give up. |
| if (TI == nullptr) { |
| DEBUG(std::cerr << "Invalid reference type"); |
| return nullptr; |
| } |
| |
| // Simple case: Just change the reference kind |
| if (auto *ReferenceTI = dyn_cast<ReferenceTypeInfo>(TI)) |
| return TC.getReferenceTypeInfo(Kind, ReferenceTI->getReferenceCounting()); |
| |
| if (auto *RecordTI = dyn_cast<RecordTypeInfo>(TI)) { |
| auto SubKind = RecordTI->getRecordKind(); |
| |
| // Look through optionals. |
| if (SubKind == RecordKind::SinglePayloadEnum) { |
| |
| if (Kind == ReferenceKind::Weak) { |
| auto *TI = TC.getTypeInfo(RecordTI->getFields()[0].TR); |
| return rebuildStorageTypeInfo(TI, Kind); |
| } |
| |
| // Class existentials are represented as record types. |
| // Destructure the existential and replace the "object" |
| // field with the right reference kind. |
| } else if (SubKind == RecordKind::ClassExistential) { |
| std::vector<FieldInfo> Fields; |
| for (auto &Field : RecordTI->getFields()) { |
| if (Field.Name == "object") { |
| auto *FieldTI = rebuildStorageTypeInfo(&Field.TI, Kind); |
| Fields.push_back({Field.Name, Field.Offset, Field.TR, *FieldTI}); |
| continue; |
| } |
| Fields.push_back(Field); |
| } |
| |
| return TC.makeTypeInfo<RecordTypeInfo>( |
| RecordTI->getSize(), |
| RecordTI->getAlignment(), |
| RecordTI->getStride(), |
| RecordTI->getNumExtraInhabitants(), |
| SubKind, Fields); |
| } |
| } |
| |
| // Anything else -- give up |
| DEBUG(std::cerr << "Invalid reference type"); |
| return nullptr; |
| } |
| |
| const TypeInfo * |
| visitAnyStorageTypeRef(const TypeRef *TR, ReferenceKind Kind) { |
| return rebuildStorageTypeInfo(TC.getTypeInfo(TR), Kind); |
| } |
| |
| const TypeInfo * |
| visitUnownedStorageTypeRef(const UnownedStorageTypeRef *US) { |
| return visitAnyStorageTypeRef(US->getType(), ReferenceKind::Unowned); |
| } |
| |
| const TypeInfo *visitWeakStorageTypeRef(const WeakStorageTypeRef *WS) { |
| return visitAnyStorageTypeRef(WS->getType(), ReferenceKind::Weak); |
| } |
| |
| const TypeInfo * |
| visitUnmanagedStorageTypeRef(const UnmanagedStorageTypeRef *US) { |
| return visitAnyStorageTypeRef(US->getType(), ReferenceKind::Unmanaged); |
| } |
| |
| const TypeInfo *visitSILBoxTypeRef(const SILBoxTypeRef *SB) { |
| return TC.getReferenceTypeInfo(ReferenceKind::Strong, |
| ReferenceCounting::Native); |
| } |
| |
| const TypeInfo *visitOpaqueTypeRef(const OpaqueTypeRef *O) { |
| DEBUG(std::cerr << "Can't lower opaque TypeRef"); |
| return nullptr; |
| } |
| }; |
| |
| const TypeInfo *TypeConverter::getTypeInfo(const TypeRef *TR) { |
| // See if we already computed the result |
| auto found = Cache.find(TR); |
| if (found != Cache.end()) |
| return found->second; |
| |
| // Detect invalid recursive value types (IRGen should not emit |
| // them in the first place, but there might be bugs) |
| if (!RecursionCheck.insert(TR).second) { |
| DEBUG(std::cerr << "TypeRef recursion detected"); |
| return nullptr; |
| } |
| |
| // Compute the result and cache it |
| auto *TI = LowerType(*this).visit(TR); |
| Cache[TR] = TI; |
| |
| RecursionCheck.erase(TR); |
| |
| return TI; |
| } |
| |
| const TypeInfo *TypeConverter::getClassInstanceTypeInfo(const TypeRef *TR, |
| unsigned start) { |
| const FieldDescriptor *FD = getBuilder().getFieldTypeInfo(TR); |
| if (FD == nullptr) { |
| DEBUG(std::cerr << "No field descriptor: "; TR->dump()); |
| return nullptr; |
| } |
| |
| switch (FD->Kind) { |
| case FieldDescriptorKind::Class: |
| case FieldDescriptorKind::ObjCClass: { |
| // Lower the class's fields using substitutions from the |
| // TypeRef to make field types concrete. |
| RecordTypeInfoBuilder builder(*this, RecordKind::ClassInstance); |
| |
| std::vector<FieldTypeInfo> Fields; |
| if (!getBuilder().getFieldTypeRefs(TR, FD, Fields)) |
| return nullptr; |
| |
| // Start layout from the given instance start offset. This should |
| // be the superclass instance size. |
| builder.addField(start, 1, /*numExtraInhabitants=*/0); |
| |
| for (auto Field : Fields) |
| builder.addField(Field.Name, Field.TR); |
| return builder.build(); |
| } |
| case FieldDescriptorKind::Struct: |
| case FieldDescriptorKind::Enum: |
| case FieldDescriptorKind::MultiPayloadEnum: |
| case FieldDescriptorKind::ObjCProtocol: |
| case FieldDescriptorKind::ClassProtocol: |
| case FieldDescriptorKind::Protocol: |
| // Invalid field descriptor. |
| DEBUG(std::cerr << "Invalid field descriptor: "; TR->dump()); |
| return nullptr; |
| } |
| |
| swift_runtime_unreachable("Unhandled FieldDescriptorKind in switch."); |
| } |
| |
| } // namespace reflection |
| } // namespace swift |