From 19eb8d282161e1c93b8a96b5f90e48cadb804edb Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 14 Apr 2017 18:03:27 -0700 Subject: [PATCH 1/8] SIL Parser: Update for subclass existentials Tested by an upcoming patch that adds IRGen SIL tests. --- include/swift/AST/DiagnosticsParse.def | 5 +++- lib/Parse/ParseSIL.cpp | 33 +++++++++++++++++--------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index b51b8054c986d..169d1633bfe80 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -528,7 +528,10 @@ ERROR(sil_member_decl_type_mismatch,none, "member defined with mismatching type %0 (expected %1)", (Type, Type)) ERROR(sil_substitution_mismatch,none, "substitution replacement type %0 does not conform to protocol %1", - (Type, DeclName)) + (Type, Type)) +ERROR(sil_not_class,none, + "substitution replacement type %0 is not a class type", + (Type)) ERROR(sil_missing_substitutions,none, "missing substitutions", ()) ERROR(sil_too_many_substitutions,none, diff --git a/lib/Parse/ParseSIL.cpp b/lib/Parse/ParseSIL.cpp index 3700f0246a0e1..6c0cf6529de79 100644 --- a/lib/Parse/ParseSIL.cpp +++ b/lib/Parse/ParseSIL.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/AST/ASTWalker.h" +#include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ProtocolConformance.h" @@ -1437,21 +1438,23 @@ bool SILParser::parseSubstitutions(SmallVectorImpl &parsed, /// Collect conformances by looking up the conformance from replacement /// type and protocol decl. static bool getConformancesForSubstitution(Parser &P, - ArrayRef protocols, + ArrayRef protocols, Type subReplacement, SourceLoc loc, SmallVectorImpl &conformances) { auto M = P.SF.getParentModule(); - for (auto proto : protocols) { - auto conformance = M->lookupConformance(subReplacement, proto, nullptr); + for (auto protoTy : protocols) { + auto conformance = M->lookupConformance(subReplacement, + protoTy->getDecl(), + nullptr); if (conformance) { conformances.push_back(*conformance); continue; } P.diagnose(loc, diag::sil_substitution_mismatch, subReplacement, - proto->getName()); + protoTy); return true; } @@ -1485,11 +1488,9 @@ bool getApplySubstitutionsFromParsed( parses = parses.slice(1); SmallVector conformances; - SmallVector protocols; - for (auto reqt : reqts) { - protocols.push_back(reqt.getSecondType() - ->castTo()->getDecl()); - } + SmallVector protocols; + for (auto reqt : reqts) + protocols.push_back(reqt.getSecondType()->castTo()); if (getConformancesForSubstitution(SP.P, protocols, parsed.replacement, @@ -1515,8 +1516,18 @@ bool getApplySubstitutionsFromParsed( static ArrayRef collectExistentialConformances(Parser &P, CanType conformingType, SourceLoc loc, CanType protocolType) { - SmallVector protocols; - protocolType.getExistentialTypeProtocols(protocols); + auto layout = protocolType.getExistentialLayout(); + + if (layout.requiresClass) { + if (!conformingType->mayHaveSuperclass() && + !conformingType->isObjCExistentialType()) { + P.diagnose(loc, diag::sil_not_class, conformingType); + } + } + + // FIXME: Check superclass also. + + auto protocols = layout.getProtocols(); if (protocols.empty()) return {}; From 497336c4bca9603a1cb4b25b2937e5b1414ffe3b Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 14 Apr 2017 17:39:18 -0700 Subject: [PATCH 2/8] IRGen: Use the right reference counting for subclass existentials --- lib/AST/Type.cpp | 19 +++-- lib/IRGen/GenArchetype.cpp | 34 ++++----- lib/IRGen/GenClass.cpp | 23 +++--- lib/IRGen/GenClass.h | 10 +-- lib/IRGen/GenDecl.cpp | 47 ++++++------ lib/IRGen/GenExistential.cpp | 42 ++++++----- lib/IRGen/GenMeta.cpp | 37 ++++------ lib/IRGen/GenReflection.cpp | 3 +- test/IRGen/subclass_existentials.sil | 71 +++++++++++++++++++ .../IRGen/type_layout_reference_storage.swift | 37 +++++++++- .../type_layout_reference_storage_objc.swift | 34 ++++++++- 11 files changed, 247 insertions(+), 110 deletions(-) create mode 100644 test/IRGen/subclass_existentials.sil diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 7bd990c2e9f2e..468b84914d293 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3977,7 +3977,6 @@ static bool doesOpaqueClassUseNativeReferenceCounting(const ASTContext &ctx) { static bool usesNativeReferenceCounting(ClassDecl *theClass, ResilienceExpansion resilience) { - // NOTE: if you change this, change irgen::getReferenceCountingForClass. // TODO: Resilience? there might be some legal avenue of changing this. while (Type supertype = theClass->getSuperclass()) { theClass = supertype->getClassOrBoundGenericClass(); @@ -3987,8 +3986,6 @@ static bool usesNativeReferenceCounting(ClassDecl *theClass, } bool TypeBase::usesNativeReferenceCounting(ResilienceExpansion resilience) { - assert(allowsOwnership()); - CanType type = getCanonicalType(); switch (type->getKind()) { #define SUGARED_TYPE(id, parent) case TypeKind::id: @@ -4011,6 +4008,10 @@ bool TypeBase::usesNativeReferenceCounting(ResilienceExpansion resilience) { return ::usesNativeReferenceCounting( cast(type)->getDecl(), resilience); + case TypeKind::UnboundGeneric: + return ::usesNativeReferenceCounting( + cast(cast(type)->getDecl()), + resilience); case TypeKind::DynamicSelf: return cast(type).getSelfType() @@ -4018,17 +4019,23 @@ bool TypeBase::usesNativeReferenceCounting(ResilienceExpansion resilience) { case TypeKind::Archetype: { auto archetype = cast(type); - assert(archetype->requiresClass()); + auto layout = archetype->getLayoutConstraint(); + assert(archetype->requiresClass() || + (layout && layout->isRefCounted())); if (auto supertype = archetype->getSuperclass()) return supertype->usesNativeReferenceCounting(resilience); return ::doesOpaqueClassUseNativeReferenceCounting(type->getASTContext()); } case TypeKind::Protocol: - case TypeKind::ProtocolComposition: + case TypeKind::ProtocolComposition: { + auto layout = getExistentialLayout(); + assert(layout.requiresClass && "Opaque existentials don't use refcounting"); + if (layout.superclass) + return layout.superclass->usesNativeReferenceCounting(resilience); return ::doesOpaqueClassUseNativeReferenceCounting(type->getASTContext()); + } - case TypeKind::UnboundGeneric: case TypeKind::Function: case TypeKind::GenericFunction: case TypeKind::SILFunction: diff --git a/lib/IRGen/GenArchetype.cpp b/lib/IRGen/GenArchetype.cpp index 47c8b94abb3a5..a0d9a933b6047 100644 --- a/lib/IRGen/GenArchetype.cpp +++ b/lib/IRGen/GenArchetype.cpp @@ -277,32 +277,28 @@ llvm::Value *irgen::emitAssociatedTypeMetadataRef(IRGenFunction &IGF, const TypeInfo *TypeConverter::convertArchetypeType(ArchetypeType *archetype) { assert(isExemplarArchetype(archetype) && "lowering non-exemplary archetype"); - LayoutConstraint LayoutInfo = archetype->getLayoutConstraint(); + auto layout = archetype->getLayoutConstraint(); // If the archetype is class-constrained, use a class pointer // representation. if (archetype->requiresClass() || - (LayoutInfo && LayoutInfo->isRefCounted())) { - ReferenceCounting refcount; - llvm::PointerType *reprTy; + (layout && layout->isRefCounted())) { + auto refcount = getReferenceCountingForType(IGM, CanType(archetype)); - if (!IGM.ObjCInterop) { - refcount = ReferenceCounting::Native; - reprTy = IGM.RefCountedPtrTy; - } else { - refcount = ReferenceCounting::Unknown; - reprTy = IGM.UnknownRefCountedPtrTy; - } + llvm::PointerType *reprTy; // If the archetype has a superclass constraint, it has at least the // retain semantics of its superclass, and it can be represented with // the supertype's pointer type. - if (Type super = archetype->getSuperclass()) { - ClassDecl *superClass = super->getClassOrBoundGenericClass(); - refcount = getReferenceCountingForClass(IGM, superClass); - + if (auto super = archetype->getSuperclass()) { auto &superTI = IGM.getTypeInfoForUnlowered(super); reprTy = cast(superTI.StorageType); + } else { + if (refcount == ReferenceCounting::Native) { + reprTy = IGM.RefCountedPtrTy; + } else { + reprTy = IGM.UnknownRefCountedPtrTy; + } } // As a hack, assume class archetypes never have spare bits. There's a @@ -320,9 +316,9 @@ const TypeInfo *TypeConverter::convertArchetypeType(ArchetypeType *archetype) { // If the archetype is trivial fixed-size layout-constrained, use a fixed size // representation. - if (LayoutInfo && LayoutInfo->isFixedSizeTrivial()) { - Size size(LayoutInfo->getTrivialSizeInBytes()); - Alignment align(LayoutInfo->getTrivialSizeInBytes()); + if (layout && layout->isFixedSizeTrivial()) { + Size size(layout->getTrivialSizeInBytes()); + Alignment align(layout->getTrivialSizeInBytes()); auto spareBits = SpareBitVector::getConstant(size.getValueInBits(), false); // Get an integer type of the required size. @@ -336,7 +332,7 @@ const TypeInfo *TypeConverter::convertArchetypeType(ArchetypeType *archetype) { // If the archetype is a trivial layout-constrained, use a POD // representation. This type is not loadable, but it is known // to be a POD. - if (LayoutInfo && LayoutInfo->isAddressOnlyTrivial()) { + if (layout && layout->isAddressOnlyTrivial()) { // TODO: Create NonFixedSizeArchetypeTypeInfo and return it. } diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 466d3dbc9697e..10ac14428cbbc 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -63,20 +63,25 @@ static ClassDecl *getRootClass(ClassDecl *theClass) { return theClass; } -/// What reference counting mechanism does a class have? -ReferenceCounting irgen::getReferenceCountingForClass(IRGenModule &IGM, - ClassDecl *theClass) { +/// What reference counting mechanism does a class-like type have? +ReferenceCounting irgen::getReferenceCountingForType(IRGenModule &IGM, + CanType type) { // If ObjC interop is disabled, we have a Swift refcount. if (!IGM.ObjCInterop) return ReferenceCounting::Native; - // NOTE: if you change this, change Type::usesNativeReferenceCounting. - // If the root class is implemented in swift, then we have a swift - // refcount; otherwise, we have an ObjC refcount. - if (getRootClass(theClass)->hasKnownSwiftImplementation()) + if (type->usesNativeReferenceCounting(ResilienceExpansion::Maximal)) return ReferenceCounting::Native; - return ReferenceCounting::ObjC; + // Class-constrained archetypes and existentials that don't use + // native reference counting and yet have a superclass must be + // using ObjC reference counting. + auto superclass = type->getSuperclass(nullptr); + if (superclass) + return ReferenceCounting::ObjC; + + // Otherwise, it could be either one. + return ReferenceCounting::Unknown; } /// What isa encoding mechanism does a type have? @@ -2087,7 +2092,7 @@ const TypeInfo * TypeConverter::convertClassType(CanType type, ClassDecl *D) { llvm::StructType *ST = IGM.createNominalType(type); llvm::PointerType *irType = ST->getPointerTo(); - ReferenceCounting refcount = ::getReferenceCountingForClass(IGM, D); + ReferenceCounting refcount = ::getReferenceCountingForType(IGM, type); SpareBitVector spareBits; diff --git a/lib/IRGen/GenClass.h b/lib/IRGen/GenClass.h index b3cbf190151e4..8beb0743e2883 100644 --- a/lib/IRGen/GenClass.h +++ b/lib/IRGen/GenClass.h @@ -132,11 +132,11 @@ namespace irgen { /// correspond to the runtime alignment of instances of the class. llvm::Constant *tryEmitClassConstantFragileInstanceAlignMask(IRGenModule &IGM, ClassDecl *theClass); - - /// What reference counting mechanism does a class use? - ReferenceCounting getReferenceCountingForClass(IRGenModule &IGM, - ClassDecl *theClass); - + + /// What reference counting mechanism does a class-like type use? + ReferenceCounting getReferenceCountingForType(IRGenModule &IGM, + CanType type); + /// What isa-encoding mechanism does a type use? IsaEncoding getIsaEncodingForType(IRGenModule &IGM, CanType type); diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 3a2b5fb47e252..29991611d1d51 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -332,22 +332,22 @@ class ObjCProtocolInitializerVisitor NewProto = Builder.CreateCall(objc_allocateProtocol, protocolName); // Add the parent protocols. - // - // FIXME: Look at the requirement signature instead. - for (auto inherited : proto->getInherited()) { - SmallVector protocols; - inherited.getType()->getExistentialTypeProtocols(protocols); - for (auto parentProto : protocols) { - if (!parentProto->isObjC()) - continue; - llvm::Value *parentRef - = IGM.getAddrOfObjCProtocolRef(parentProto, NotForDefinition); - parentRef = IGF.Builder.CreateBitCast(parentRef, - IGM.ProtocolDescriptorPtrTy->getPointerTo()); - auto parent = Builder.CreateLoad(parentRef, - IGM.getPointerAlignment()); - Builder.CreateCall(protocol_addProtocol, {NewProto, parent}); - } + auto *requirementSig = proto->getRequirementSignature(); + auto conformsTo = + requirementSig->getConformsTo(proto->getSelfInterfaceType(), + *IGF.IGM.getSwiftModule()); + + for (auto parentProto : conformsTo) { + if (!parentProto->isObjC()) + continue; + llvm::Value *parentRef = IGM.getAddrOfObjCProtocolRef(parentProto, + NotForDefinition); + parentRef = IGF.Builder.CreateBitCast(parentRef, + IGM.ProtocolDescriptorPtrTy + ->getPointerTo()); + auto parent = Builder.CreateLoad(parentRef, + IGM.getPointerAlignment()); + Builder.CreateCall(protocol_addProtocol, {NewProto, parent}); } // Add the members. @@ -616,16 +616,17 @@ void IRGenModule::emitRuntimeRegistration() { llvm::SmallVector protoInitOrder; - // FIXME: Use the requirement signature instead. std::function orderProtocol = [&](ProtocolDecl *proto) { + auto *requirementSig = proto->getRequirementSignature(); + auto conformsTo = requirementSig->getConformsTo( + proto->getSelfInterfaceType(), + *getSwiftModule()); + // Recursively put parents first. - for (auto &inherited : proto->getInherited()) { - SmallVector parents; - inherited.getType()->getExistentialTypeProtocols(parents); - for (auto parent : parents) - orderProtocol(parent); - } + for (auto parent : conformsTo) + orderProtocol(parent); + // Skip if we don't need to reify this protocol. auto found = protos.find(proto); if (found == protos.end()) diff --git a/lib/IRGen/GenExistential.cpp b/lib/IRGen/GenExistential.cpp index a7b1a0cfde157..05a7ed9dd5b0c 100644 --- a/lib/IRGen/GenExistential.cpp +++ b/lib/IRGen/GenExistential.cpp @@ -1088,6 +1088,12 @@ class ClassExistentialTypeInfo final public: + llvm::PointerType *getPayloadType() const { + auto *ty = getStorageType(); + llvm::StructType *structTy = cast(ty); + return cast(structTy->elements()[0]); + } + bool isSingleRetainablePointer(ResilienceExpansion expansion, ReferenceCounting *refcounting) const override{ if (refcounting) *refcounting = Refcounting; @@ -1388,7 +1394,6 @@ static const TypeInfo *createExistentialTypeInfo(IRGenModule &IGM, CanType T) { // The existential container is class-constrained if any of its protocol // constraints are. - bool requiresClass = layout.requiresClass; bool allowsTaggedPointers = true; for (auto protoTy : layout.getProtocols()) { @@ -1412,21 +1417,23 @@ static const TypeInfo *createExistentialTypeInfo(IRGenModule &IGM, CanType T) { // If the existential is class, lower it to a class // existential representation. - if (requiresClass) { + if (layout.requiresClass) { // If we're not using the Objective-C runtime, we can use the // native reference counting entry points. - ReferenceCounting refcounting; - - // FIXME: If there is a superclass constraint we might be able to - // use native refcounting. - if (!IGM.ObjCInterop) { - refcounting = ReferenceCounting::Native; - fields[1] = IGM.RefCountedPtrTy; + ReferenceCounting refcounting = getReferenceCountingForType(IGM, T); + + llvm::PointerType *reprTy = nullptr; + if (layout.superclass) { + auto &superTI = IGM.getTypeInfoForUnlowered(layout.superclass); + reprTy = cast(superTI.getStorageType()); + } else if (refcounting == ReferenceCounting::Native) { + reprTy = IGM.RefCountedPtrTy; } else { - refcounting = ReferenceCounting::Unknown; - fields[1] = IGM.UnknownRefCountedPtrTy; + reprTy = IGM.UnknownRefCountedPtrTy; } + fields[1] = reprTy; + // Replace the type metadata pointer with the class instance. auto classFields = llvm::makeArrayRef(fields).slice(1); type->setBody(classFields); @@ -1438,7 +1445,9 @@ static const TypeInfo *createExistentialTypeInfo(IRGenModule &IGM, CanType T) { // The class pointer is an unknown heap object, so it may be a tagged // pointer, if the platform has those. - if (allowsTaggedPointers && IGM.TargetInfo.hasObjCTaggedPointers()) { + if (allowsTaggedPointers && + refcounting != ReferenceCounting::Native && + IGM.TargetInfo.hasObjCTaggedPointers()) { spareBits.appendClearBits(IGM.getPointerSize().getValueInBits()); } else { // If the platform doesn't use ObjC tagged pointers, we can go crazy. @@ -1809,13 +1818,8 @@ void irgen::emitClassExistentialContainer(IRGenFunction &IGF, auto &destTI = IGF.getTypeInfo(outType).as(); // Cast the instance pointer to an opaque refcounted pointer. - llvm::Value *opaqueInstance; - if (!IGF.IGM.ObjCInterop) - opaqueInstance = IGF.Builder.CreateBitCast(instance, - IGF.IGM.RefCountedPtrTy); - else - opaqueInstance = IGF.Builder.CreateBitCast(instance, - IGF.IGM.UnknownRefCountedPtrTy); + auto opaqueInstance = IGF.Builder.CreateBitCast(instance, + destTI.getPayloadType()); out.add(opaqueInstance); // Emit the witness table pointers. diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 0fa44a17c2072..3a12fd95422cd 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -1906,7 +1906,8 @@ namespace { llvm::Value *visitAnyClassType(ClassDecl *classDecl) { // All class types have the same layout. - switch (getReferenceCountingForClass(IGF.IGM, classDecl)) { + auto type = classDecl->getDeclaredType()->getCanonicalType(); + switch (getReferenceCountingForType(IGF.IGM, type)) { case ReferenceCounting::Native: return emitFromValueWitnessTable(IGF.IGM.Context.TheNativeObjectType); @@ -1953,39 +1954,26 @@ namespace { // Reference storage types with witness tables need open-coded layouts. // TODO: Maybe we could provide prefabs for 1 witness table. if (referent.isExistentialType()) { - SmallVector protocols; - referent.getExistentialTypeProtocols(protocols); - for (auto *proto : protocols) - if (IGF.getSILTypes().protocolRequiresWitnessTable(proto)) + auto layout = referent.getExistentialLayout(); + for (auto *protoTy : layout.getProtocols()) { + auto *protoDecl = protoTy->getDecl(); + if (IGF.getSILTypes().protocolRequiresWitnessTable(protoDecl)) return visitType(type); + } } // Unmanaged references are plain pointers with extra inhabitants, // which look like thick metatypes. + // + // FIXME: This sounds wrong, an Objective-C tagged pointer could be + // stored in an unmanaged reference for instance. if (type->getOwnership() == Ownership::Unmanaged) { auto metatype = CanMetatypeType::get(C.TheNativeObjectType); return emitFromValueWitnessTable(metatype); } - auto getReferenceCountingForReferent - = [&](CanType referent) -> ReferenceCounting { - // If Objective-C interop is enabled, generic types might contain - // Objective-C references, so we have to use unknown reference - // counting. - if (isa(referent) || - referent->isExistentialType()) - return (IGF.IGM.ObjCInterop ? - ReferenceCounting::Unknown : - ReferenceCounting::Native); - - if (auto classDecl = referent->getClassOrBoundGenericClass()) - return getReferenceCountingForClass(IGF.IGM, classDecl); - - llvm_unreachable("unexpected referent for ref storage type"); - }; - CanType valueWitnessReferent; - switch (getReferenceCountingForReferent(referent)) { + switch (getReferenceCountingForType(IGF.IGM, referent)) { case ReferenceCounting::Unknown: case ReferenceCounting::Block: case ReferenceCounting::ObjC: @@ -3355,7 +3343,8 @@ namespace { ClassFlags flags = ClassFlags::IsSwift1; // Set a flag if the class uses Swift 1.0 refcounting. - if (getReferenceCountingForClass(IGM, Target) + auto type = Target->getDeclaredType()->getCanonicalType(); + if (getReferenceCountingForType(IGM, type) == ReferenceCounting::Native) { flags |= ClassFlags::UsesSwift1Refcounting; } diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 26674d49234ea..79d4aeb0ac2a6 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -388,7 +388,8 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { auto kind = FieldDescriptorKind::Struct; if (auto CD = dyn_cast(NTD)) { - auto RC = getReferenceCountingForClass(IGM, const_cast(CD)); + auto type = CD->getDeclaredType()->getCanonicalType(); + auto RC = getReferenceCountingForType(IGM, type); if (RC == ReferenceCounting::ObjC) kind = FieldDescriptorKind::ObjCClass; else diff --git a/test/IRGen/subclass_existentials.sil b/test/IRGen/subclass_existentials.sil new file mode 100644 index 0000000000000..f91e0d18c13b0 --- /dev/null +++ b/test/IRGen/subclass_existentials.sil @@ -0,0 +1,71 @@ +// RUN: %target-swift-frontend -primary-file %s -emit-ir -enable-experimental-subclass-existentials | %FileCheck %s --check-prefix=CHECK-%target-runtime --check-prefix=CHECK + +sil_stage canonical + +import Builtin +import Swift + +class C {} +protocol P {} + +class D : C, P {} + +// Make sure we use native reference counting when the existential has a Swift +// class bound. + +// CHECK-objc-LABEL: define{{( protected)?}} swiftcc void @checkRefcounting(%T21subclass_existentials1CC*, i8**, %objc_object*, i8**) +// CHECK-native-LABEL: define{{( protected)?}} swiftcc void @checkRefcounting(%T21subclass_existentials1CC*, i8**, %swift.refcounted*, i8**) +// CHECK-NEXT: entry: +// CHECK-objc-NEXT: call void @swift_unknownRelease(%objc_object* %2) +// CHECK-native-NEXT: call void @swift_rt_swift_release(%swift.refcounted* %2) +// CHECK-NEXT: call void bitcast (void (%swift.refcounted*)* @swift_rt_swift_release to void (%T21subclass_existentials1CC*)*)(%T21subclass_existentials1CC* %0) +// CHECK-NEXT: ret void + +sil @checkRefcounting : $@convention(thin) (@owned C & P, @owned AnyObject & P) -> () { +bb0(%0 : $C & P, %1 : $AnyObject & P): + destroy_value %1 : $AnyObject & P + destroy_value %0 : $C & P + %6 = tuple () + return %6 : $() +} + +// Make sure we call the runtime function with the right arguments when we +// instantiate metadata for a subclass existential. + +sil @takesMetadata : $@convention(thin) (@thick T.Type) -> () + +// CHECK-LABEL: define{{( protected)?}} swiftcc void @checkMetadata() +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = call %swift.type* @_T021subclass_existentials1P_AA1CCXcMa() +// CHECK-NEXT: call swiftcc void @takesMetadata(%swift.type* %0, %swift.type* %0) +// CHECK-NEXT: ret void + +// CHECK-LABEL: define linkonce_odr hidden %swift.type* @_T021subclass_existentials1P_AA1CCXcMa() +// CHECK: entry: +// CHECK-NEXT: [[PROTOCOL_ARRAY:%.*]] = alloca [1 x %swift.protocol*] +// CHECK: cacheIsNull: +// CHECK: [[PROTOCOLS:%.*]] = bitcast [1 x %swift.protocol*]* [[PROTOCOL_ARRAY]] to %swift.protocol** +// CHECK-NEXT: [[PROTOCOL:%.*]] = getelementptr inbounds %swift.protocol*, %swift.protocol** [[PROTOCOLS]], i32 0 +// CHECK-NEXT: store %swift.protocol* @_T021subclass_existentials1PMp, %swift.protocol** [[PROTOCOL]] +// CHECK-NEXT: [[SUPERCLASS:%.*]] = call %swift.type* @_T021subclass_existentials1CCMa() +// CHECK-NEXT: [[METATYPE:%.*]] = call %swift.type* @swift_rt_swift_getExistentialTypeMetadata(i1 false, %swift.type* [[SUPERCLASS]], {{i32|i64}} 1, %swift.protocol** [[PROTOCOLS]]) +// CHECK: ret + +sil @checkMetadata : $@convention(thin) () -> () { +bb0: + %0 = function_ref @takesMetadata : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> () + %1 = metatype $@thin (C & P).Protocol + %2 = metatype $@thick (C & P).Protocol + %3 = apply %0<(C & P)>(%2) : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> () + %4 = tuple () + return %4 : $() +} + +sil @eraseToExistential : $@convention(thin) (@owned D) -> @owned C & P { +bb0(%0 : $D): + %2 = init_existential_ref %0 : $D : $D, $C & P + return %2 : $C & P +} + +sil_vtable C {} +sil_vtable D {} \ No newline at end of file diff --git a/test/IRGen/type_layout_reference_storage.swift b/test/IRGen/type_layout_reference_storage.swift index 65ede45fa1657..bfc93b0ac5720 100644 --- a/test/IRGen/type_layout_reference_storage.swift +++ b/test/IRGen/type_layout_reference_storage.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -enable-experimental-subclass-existentials %s | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK class C {} protocol P: class {} @@ -6,7 +6,7 @@ protocol Q: class {} // CHECK: @_T029type_layout_reference_storage26ReferenceStorageTypeLayoutVMP = internal global {{.*}} @create_generic_metadata_ReferenceStorageTypeLayout // CHECK: define private %swift.type* @create_generic_metadata_ReferenceStorageTypeLayout -struct ReferenceStorageTypeLayout { +struct ReferenceStorageTypeLayout { var z: T // -- Known-Swift-refcounted type @@ -19,6 +19,16 @@ struct ReferenceStorageTypeLayout { // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BoSgXwWV, i32 17) weak var cwi: C! + // -- Known-Swift-refcounted archetype + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BoXoWV, i32 17) + unowned(safe) var nc: Native + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BomWV, i32 17) + unowned(unsafe) var nu: Native + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BoSgXwWV, i32 17) + weak var nwo: Native? + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BoSgXwWV, i32 17) + weak var nwi: Native! + // -- Open-code layout for protocol types with witness tables. // Note that the layouts for unowned(safe) references are // only bitwise takable when ObjC interop is disabled. @@ -48,6 +58,19 @@ struct ReferenceStorageTypeLayout { // CHECK-32: store i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @type_layout_12_4_[[WEAK_XI]], i32 0, i32 0) weak var pqwi: (P & Q)! + // CHECK-64: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_24_8_[[UNOWNED_XI]]{{(,|_bt,)}} i32 0, i32 0) + // CHECK-32: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_12_4_[[UNOWNED_XI]]{{(,|_bt,)}} i32 0, i32 0) + unowned(safe) var pqcs: P & Q & C + // CHECK-64: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_24_8_[[REF_XI]]_pod, i32 0, i32 0) + // CHECK-32: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_12_4_[[REF_XI]]_pod, i32 0, i32 0) + unowned(unsafe) var pqcu: P & Q & C + // CHECK-64: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_24_8_[[WEAK_XI]], i32 0, i32 0) + // CHECK-32: store i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @type_layout_12_4_[[WEAK_XI]], i32 0, i32 0) + weak var pqcwo: (P & Q & C)? + // CHECK-64: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_24_8_[[WEAK_XI]], i32 0, i32 0) + // CHECK-32: store i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @type_layout_12_4_[[WEAK_XI]], i32 0, i32 0) + weak var pqcwi: (P & Q & C)! + // -- Unknown-refcounted existential without witness tables. // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0[[UNKNOWN:B[Oo]]]XoWV, i32 17) unowned(safe) var aos: AnyObject @@ -57,4 +80,14 @@ struct ReferenceStorageTypeLayout { weak var aowo: AnyObject? // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0[[UNKNOWN]]SgXwWV, i32 17) weak var aowi: AnyObject! + + // -- Unknown-refcounted archetype + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0[[UNKNOWN:B[Oo]]]XoWV, i32 17) + unowned(safe) var us: Unknown + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BomWV, i32 17) + unowned(unsafe) var uu: Unknown + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0[[UNKNOWN]]SgXwWV, i32 17) + weak var uwo: Unknown? + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0[[UNKNOWN]]SgXwWV, i32 17) + weak var uwi: Unknown! } diff --git a/test/IRGen/type_layout_reference_storage_objc.swift b/test/IRGen/type_layout_reference_storage_objc.swift index f6db95b22b02b..75a0558d6d415 100644 --- a/test/IRGen/type_layout_reference_storage_objc.swift +++ b/test/IRGen/type_layout_reference_storage_objc.swift @@ -1,8 +1,9 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-ir %s -enable-experimental-subclass-existentials | %FileCheck %s // REQUIRES: objc_interop import Foundation +class NativeClass {} class C: NSObject {} @objc protocol P {} @objc protocol Q {} @@ -10,7 +11,7 @@ protocol NonObjC: class {} // CHECK: @_T034type_layout_reference_storage_objc26ReferenceStorageTypeLayoutVMP = internal global {{.*}} @create_generic_metadata_ReferenceStorageTypeLayout // CHECK: define private %swift.type* @create_generic_metadata_ReferenceStorageTypeLayout -struct ReferenceStorageTypeLayout { +struct ReferenceStorageTypeLayout { var z: T // -- ObjC-refcounted class @@ -23,6 +24,16 @@ struct ReferenceStorageTypeLayout { // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BOSgXwWV, i32 17) weak var cwi: C! + // -- ObjC-refcounted archetype + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BOXoWV, i32 17) + unowned(safe) var os: ObjC + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BomWV, i32 17) + unowned(unsafe) var ou: ObjC + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BOSgXwWV, i32 17) + weak var owo: ObjC? + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BOSgXwWV, i32 17) + weak var owi: ObjC! + // -- Pure ObjC protocols are unknown-refcounted // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BOXoWV, i32 17) unowned(safe) var ps: P @@ -42,6 +53,16 @@ struct ReferenceStorageTypeLayout { // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BOSgXwWV, i32 17) weak var pqwi: (P & Q)! + // -- Composition with ObjC protocol and native class is native-refcounted + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BoXoWV, i32 17) + unowned(safe) var pncs: (P & NativeClass) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BomWV, i32 17) + unowned(unsafe) var pncu: (P & NativeClass) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BoSgXwWV, i32 17) + weak var pncwo: (P & NativeClass)? + // CHECK: store i8** getelementptr inbounds (i8*, i8** @_T0BoSgXwWV, i32 17) + weak var pncwi: (P & NativeClass)! + // -- Open-code layouts when there are witness tables. // CHECK-64: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_16_8_7fffffff_bt, i32 0, i32 0) unowned(safe) var pqrs: P & Q & NonObjC @@ -51,4 +72,13 @@ struct ReferenceStorageTypeLayout { weak var pqrwo: (P & Q & NonObjC)? // CHECK-64: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_16_8_1, i32 0, i32 0) weak var pqrwi: (P & Q & NonObjC)! + + // CHECK-64: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_16_8_7fffffff_bt, i32 0, i32 0) + unowned(safe) var pqrncs: P & Q & NonObjC & NativeClass + // CHECK-64: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_16_8_7fffffff_pod, i32 0, i32 0) + unowned(unsafe) var pqrncu: P & Q & NonObjC & NativeClass + // CHECK-64: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_16_8_1, i32 0, i32 0) + weak var pqrncwo: (P & Q & NonObjC & NativeClass)? + // CHECK-64: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_16_8_1, i32 0, i32 0) + weak var pqrncwi: (P & Q & NonObjC & NativeClass)! } From 6580e5055f589de59fb49f1852759c7958359f2f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 14 Apr 2017 18:23:35 -0700 Subject: [PATCH 3/8] AST Verifier: Remove a usage of getExistentialTypeProtocols() --- lib/AST/ASTVerifier.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 3212f601de27b..b092d805e1fe1 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -1128,11 +1128,10 @@ class Verifier : public ASTWalker { abort(); } - SmallVector protocols; - srcTy->getExistentialTypeProtocols(protocols); - - if (protocols.size() != 1 - || !protocols[0]->isObjC()) { + auto layout = srcTy->getExistentialLayout(); + if (layout.superclass || + !layout.isObjC() || + layout.getProtocols().size() != 1) { Out << "ProtocolMetatypeToObject with non-ObjC-protocol metatype:\n"; E->print(Out); Out << "\n"; From 61e0c558aa3edd73df5b50a1a318d2273dedd01c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 14 Apr 2017 18:28:26 -0700 Subject: [PATCH 4/8] FrontendTool: Remove a usage of getExistentialTypeProtocols() --- lib/FrontendTool/ReferenceDependencies.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/FrontendTool/ReferenceDependencies.cpp b/lib/FrontendTool/ReferenceDependencies.cpp index aa9c374c0850f..ce2c1434867aa 100644 --- a/lib/FrontendTool/ReferenceDependencies.cpp +++ b/lib/FrontendTool/ReferenceDependencies.cpp @@ -16,6 +16,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/ExistentialLayout.h" #include "swift/AST/Module.h" #include "swift/AST/ModuleLoader.h" #include "swift/AST/NameLookup.h" @@ -85,18 +86,24 @@ static bool declIsPrivate(const Decl *member) { } static bool extendedTypeIsPrivate(TypeLoc inheritedType) { - if (!inheritedType.getType()) + auto type = inheritedType.getType(); + if (!type) return true; - if (!inheritedType.getType()->isExistentialType()) { + if (!type->isExistentialType()) { // Be conservative. We don't know how to deal with other extended types. return false; } - SmallVector protocols; - inheritedType.getType()->getExistentialTypeProtocols(protocols); + auto layout = type->getExistentialLayout(); + assert(!layout.superclass && "Should not have a subclass existential " + "in the inheritance clause of an extension"); + for (auto protoTy : layout.getProtocols()) { + if (!declIsPrivate(protoTy->getDecl())) + return false; + } - return std::all_of(protocols.begin(), protocols.end(), declIsPrivate); + return true; } static std::string mangleTypeAsContext(const NominalTypeDecl *type) { @@ -172,6 +179,8 @@ bool swift::emitReferenceDependencies(DiagnosticEngine &diags, break; } + // Check if the extension is just adding members, or if it is + // introducing a conformance to a public protocol. bool justMembers = std::all_of(ED->getInherited().begin(), ED->getInherited().end(), extendedTypeIsPrivate); From ff20d0661b4c5ae265e55b894a7bc177d2b8d01e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 14 Apr 2017 18:37:19 -0700 Subject: [PATCH 5/8] Sema: Remove a usage of getExistentialTypeProtocols() --- lib/Sema/ITCDecl.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/Sema/ITCDecl.cpp b/lib/Sema/ITCDecl.cpp index 9ba5be4cecd04..de4e1a4e4450c 100644 --- a/lib/Sema/ITCDecl.cpp +++ b/lib/Sema/ITCDecl.cpp @@ -19,6 +19,7 @@ #include "swift/Sema/IterativeTypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" +#include "swift/AST/ExistentialLayout.h" #include using namespace swift; @@ -256,9 +257,12 @@ void IterativeTypeChecker::processInheritedProtocols( // Collect existential types. // FIXME: We'd prefer to keep what the user wrote here. if (inherited.getType()->isExistentialType()) { - SmallVector protocols; - inherited.getType()->getExistentialTypeProtocols(protocols); - for (auto inheritedProtocol: protocols) { + auto layout = inherited.getType()->getExistentialLayout(); + assert(!layout.superclass && "Need to redo inheritance clause " + "typechecking"); + for (auto inheritedProtocolTy: layout.getProtocols()) { + auto *inheritedProtocol = inheritedProtocolTy->getDecl(); + if (inheritedProtocol == protocol || inheritedProtocol->inheritsFrom(protocol)) { if (!diagnosedCircularity) { From ea015822c85b0fcae82c650cea5554a6f1fc7fb1 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 14 Apr 2017 18:37:58 -0700 Subject: [PATCH 6/8] ClangImporter: Remove a usage of getExistentialTypeProtocols() --- lib/ClangImporter/ImportDecl.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 78316cd968adb..2bd564b3ca38c 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -23,6 +23,7 @@ #include "swift/AST/Builtins.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsClangImporter.h" +#include "swift/AST/ExistentialLayout.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" @@ -4742,12 +4743,15 @@ static bool inheritanceListContainsProtocol(ArrayRef inherited, return llvm::any_of(inherited, [proto](TypeLoc type) -> bool { if (!type.getType()->isExistentialType()) return false; - SmallVector protos; - type.getType()->getExistentialTypeProtocols(protos); - return ProtocolType::visitAllProtocols(protos, - [proto](const ProtocolDecl *next) { - return next == proto; - }); + + auto layout = type.getType()->getExistentialLayout(); + for (auto protoTy : layout.getProtocols()) { + auto *protoDecl = protoTy->getDecl(); + if (protoDecl == proto || protoDecl->inheritsFrom(proto)) + return true; + } + + return false; }); } From 2cd6a03e5d1cadff962f957889285359c2d53fea Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 14 Apr 2017 18:38:10 -0700 Subject: [PATCH 7/8] AST: Remove getExistentialTypeProtocols() --- include/swift/AST/Type.h | 7 ------- include/swift/AST/Types.h | 4 ---- lib/AST/Type.cpp | 16 ---------------- 3 files changed, 27 deletions(-) diff --git a/include/swift/AST/Type.h b/include/swift/AST/Type.h index 9707113f72439..49a59c1519adc 100644 --- a/include/swift/AST/Type.h +++ b/include/swift/AST/Type.h @@ -351,8 +351,6 @@ class CanType : public Type { static bool isReferenceTypeImpl(CanType type, bool functionsCount); static bool isExistentialTypeImpl(CanType type); static bool isAnyExistentialTypeImpl(CanType type); - static void getExistentialTypeProtocolsImpl(CanType type, - SmallVectorImpl &protocols); static bool isObjCExistentialTypeImpl(CanType type); static CanType getAnyOptionalObjectTypeImpl(CanType type, OptionalTypeKind &kind); @@ -406,11 +404,6 @@ class CanType : public Type { return isAnyExistentialTypeImpl(*this); } - /// Given that this type is an existential, return its - /// protocols in a canonical order. - void getExistentialTypeProtocols( - SmallVectorImpl &protocols); - /// Break an existential down into a set of constraints. ExistentialLayout getExistentialLayout(); diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 56edc38cf9c6f..4440a833365e9 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -586,10 +586,6 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// bound. bool isClassExistentialType(); - /// Given that this type is an existential type, produce - /// its list of protocols. - void getExistentialTypeProtocols(SmallVectorImpl &protocols); - /// Break an existential down into a set of constraints. ExistentialLayout getExistentialLayout(); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 468b84914d293..ce1ebb5a096b7 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -208,22 +208,6 @@ bool TypeBase::allowsOwnership() { return getCanonicalType().isAnyClassReferenceType(); } -void TypeBase::getExistentialTypeProtocols( - SmallVectorImpl &protocols) { - getCanonicalType().getExistentialTypeProtocols(protocols); -} - -void CanType::getExistentialTypeProtocols( - SmallVectorImpl &protocols) { - // FIXME: Remove this completely - auto layout = getExistentialLayout(); - assert(!layout.superclass && "Subclass existentials not fully supported yet"); - assert((!layout.requiresClass || layout.requiresClassImplied) && - "Explicit AnyObject should not appear yet"); - for (auto proto : layout.getProtocols()) - protocols.push_back(proto->getDecl()); -} - ExistentialLayout::ExistentialLayout(ProtocolType *type) { assert(type->isCanonical()); From 03373a1d0c5d54ad4995f3ab547db91bfb711e9c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 14 Apr 2017 18:38:22 -0700 Subject: [PATCH 8/8] AST: Micro-optimize ProtocolType::visitAllProtocols() --- lib/AST/Type.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index ce1ebb5a096b7..ccabfbe596abc 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2621,7 +2621,7 @@ void ArchetypeType::populateNestedTypes() const { ProtocolType::visitAllProtocols(getConformsTo(), [&](ProtocolDecl *proto) -> bool { // Objective-C protocols don't have type members. - if (proto->hasClangNode()) return false; + if (proto->isObjC()) return false; for (auto member : proto->getMembers()) { if (auto assocType = dyn_cast(member)) {