/// Emit a checked cast to a protocol or protocol composition. void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF, llvm::Value *value, SILType srcType, SILType destType, CheckedCastMode mode, Optional<MetatypeRepresentation> metatypeKind, Explosion &ex) { SmallVector<ProtocolDecl*, 4> allProtos; destType.getSwiftRValueType().getAnyExistentialTypeProtocols(allProtos); // Look up witness tables for the protocols that need them and get // references to the ObjC Protocol* values for the objc protocols. SmallVector<llvm::Value*, 4> objcProtos; SmallVector<llvm::Value*, 4> witnessTableProtos; bool hasClassConstraint = false; bool hasClassConstraintByProtocol = false; for (auto proto : allProtos) { // If the protocol introduces a class constraint, track whether we need // to check for it independent of protocol witnesses. if (proto->requiresClass()) { hasClassConstraint = true; if (proto->getKnownProtocolKind() && *proto->getKnownProtocolKind() == KnownProtocolKind::AnyObject) { // AnyObject only requires that the type be a class. continue; } // If this protocol is class-constrained but not AnyObject, checking its // conformance will check the class constraint too. hasClassConstraintByProtocol = true; } if (Lowering::TypeConverter::protocolRequiresWitnessTable(proto)) { auto descriptor = emitProtocolDescriptorRef(IGF, proto); witnessTableProtos.push_back(descriptor); } if (!proto->isObjC()) continue; objcProtos.push_back(emitReferenceToObjCProtocol(IGF, proto)); } llvm::Type *resultType; if (metatypeKind) { switch (*metatypeKind) { case MetatypeRepresentation::Thin: llvm_unreachable("can't cast to thin metatype"); case MetatypeRepresentation::Thick: resultType = IGF.IGM.TypeMetadataPtrTy; break; case MetatypeRepresentation::ObjC: resultType = IGF.IGM.ObjCClassPtrTy; break; } } else { auto schema = IGF.getTypeInfo(destType).getSchema(); resultType = schema[0].getScalarType(); } // We only need to check the class constraint for metatype casts where // no protocol conformance indirectly requires the constraint for us. bool checkClassConstraint = (bool)metatypeKind && hasClassConstraint && !hasClassConstraintByProtocol; llvm::Value *resultValue = value; // If we don't have anything we really need to check, then trivially succeed. if (objcProtos.empty() && witnessTableProtos.empty() && !checkClassConstraint) { resultValue = IGF.Builder.CreateBitCast(value, resultType); ex.add(resultValue); return; } // Check the ObjC protocol conformances if there were any. llvm::Value *objcCast = nullptr; if (!objcProtos.empty()) { // Get the ObjC instance or class object to check for these conformances. llvm::Value *objcObject; if (metatypeKind) { switch (*metatypeKind) { case MetatypeRepresentation::Thin: llvm_unreachable("can't cast to thin metatype"); case MetatypeRepresentation::Thick: { // The metadata might be for a non-class type, which wouldn't have // an ObjC class object. objcObject = nullptr; break; } case MetatypeRepresentation::ObjC: // Metatype is already an ObjC object. objcObject = value; break; } } else { // Class instance is already an ObjC object. objcObject = value; } if (objcObject) objcObject = IGF.Builder.CreateBitCast(objcObject, IGF.IGM.UnknownRefCountedPtrTy); // Pick the cast function based on the cast mode and on whether we're // casting a Swift metatype or ObjC object. llvm::Value *castFn; switch (mode) { case CheckedCastMode::Unconditional: castFn = objcObject ? IGF.IGM.getDynamicCastObjCProtocolUnconditionalFn() : IGF.IGM.getDynamicCastTypeToObjCProtocolUnconditionalFn(); break; case CheckedCastMode::Conditional: castFn = objcObject ? IGF.IGM.getDynamicCastObjCProtocolConditionalFn() : IGF.IGM.getDynamicCastTypeToObjCProtocolConditionalFn(); break; } llvm::Value *objcCastObject = objcObject ? objcObject : value; Address protoRefsBuf = IGF.createAlloca( llvm::ArrayType::get(IGF.IGM.Int8PtrTy, objcProtos.size()), IGF.IGM.getPointerAlignment(), "objc_protocols"); protoRefsBuf = IGF.Builder.CreateBitCast(protoRefsBuf, IGF.IGM.Int8PtrPtrTy); for (unsigned index : indices(objcProtos)) { Address protoRefSlot = IGF.Builder.CreateConstArrayGEP( protoRefsBuf, index, IGF.IGM.getPointerSize()); IGF.Builder.CreateStore(objcProtos[index], protoRefSlot); ++index; } objcCast = IGF.Builder.CreateCall( castFn, {objcCastObject, IGF.IGM.getSize(Size(objcProtos.size())), protoRefsBuf.getAddress()}); resultValue = IGF.Builder.CreateBitCast(objcCast, resultType); } // If we don't need to look up any witness tables, we're done. if (witnessTableProtos.empty() && !checkClassConstraint) { ex.add(resultValue); return; } // If we're doing a conditional cast, and the ObjC protocol checks failed, // then the cast is done. Optional<ConditionalDominanceScope> condition; llvm::BasicBlock *origBB = nullptr, *successBB = nullptr, *contBB = nullptr; if (!objcProtos.empty()) { switch (mode) { case CheckedCastMode::Unconditional: break; case CheckedCastMode::Conditional: { origBB = IGF.Builder.GetInsertBlock(); successBB = IGF.createBasicBlock("success"); contBB = IGF.createBasicBlock("cont"); auto isNull = IGF.Builder.CreateICmpEQ(objcCast, llvm::ConstantPointerNull::get( cast<llvm::PointerType>(objcCast->getType()))); IGF.Builder.CreateCondBr(isNull, contBB, successBB); IGF.Builder.emitBlock(successBB); condition.emplace(IGF); } } } // Get the Swift type metadata for the type. llvm::Value *metadataValue; if (metatypeKind) { switch (*metatypeKind) { case MetatypeRepresentation::Thin: llvm_unreachable("can't cast to thin metatype"); case MetatypeRepresentation::Thick: // The value is already a native metatype. metadataValue = value; break; case MetatypeRepresentation::ObjC: // Get the type metadata from the ObjC class, which may be a wrapper. metadataValue = emitObjCMetadataRefForMetadata(IGF, value); } } else { // Get the type metadata for the instance. metadataValue = emitDynamicTypeOfHeapObject(IGF, value, srcType); } // Look up witness tables for the protocols that need them. auto fn = emitExistentialScalarCastFn(IGF.IGM, witnessTableProtos.size(), mode, checkClassConstraint); llvm::SmallVector<llvm::Value *, 4> args; if (resultValue->getType() != IGF.IGM.Int8PtrTy) resultValue = IGF.Builder.CreateBitCast(resultValue, IGF.IGM.Int8PtrTy); args.push_back(resultValue); args.push_back(metadataValue); for (auto proto : witnessTableProtos) args.push_back(proto); auto valueAndWitnessTables = IGF.Builder.CreateCall(fn, args); resultValue = IGF.Builder.CreateExtractValue(valueAndWitnessTables, 0); if (resultValue->getType() != resultType) resultValue = IGF.Builder.CreateBitCast(resultValue, resultType); ex.add(resultValue); for (unsigned i = 0, e = witnessTableProtos.size(); i < e; ++i) { auto wt = IGF.Builder.CreateExtractValue(valueAndWitnessTables, i + 1); ex.add(wt); } // If we had conditional ObjC checks, join the failure paths. if (contBB) { condition.reset(); IGF.Builder.CreateBr(contBB); IGF.Builder.emitBlock(contBB); // Return null on the failure path. Explosion successEx = std::move(ex); ex.reset(); while (!successEx.empty()) { auto successVal = successEx.claimNext(); auto failureVal = llvm::Constant::getNullValue(successVal->getType()); auto phi = IGF.Builder.CreatePHI(successVal->getType(), 2); phi->addIncoming(successVal, successBB); phi->addIncoming(failureVal, origBB); ex.add(phi); } } }
/// Emit a checked cast to a protocol or protocol composition. void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF, llvm::Value *value, SILType srcType, SILType destType, CheckedCastMode mode, Optional<MetatypeRepresentation> metatypeKind, Explosion &ex) { auto srcInstanceType = srcType.getSwiftRValueType(); auto destInstanceType = destType.getSwiftRValueType(); while (auto metatypeType = dyn_cast<ExistentialMetatypeType>( destInstanceType)) { destInstanceType = metatypeType.getInstanceType(); srcInstanceType = cast<AnyMetatypeType>(srcInstanceType).getInstanceType(); } auto layout = destInstanceType.getExistentialLayout(); // Look up witness tables for the protocols that need them and get // references to the ObjC Protocol* values for the objc protocols. SmallVector<llvm::Value*, 4> objcProtos; SmallVector<llvm::Value*, 4> witnessTableProtos; bool hasClassConstraint = layout.requiresClass(); bool hasClassConstraintByProtocol = false; bool hasSuperclassConstraint = bool(layout.superclass); for (auto protoTy : layout.getProtocols()) { auto *protoDecl = protoTy->getDecl(); // If the protocol introduces a class constraint, track whether we need // to check for it independent of protocol witnesses. if (protoDecl->requiresClass()) { assert(hasClassConstraint); hasClassConstraintByProtocol = true; } if (Lowering::TypeConverter::protocolRequiresWitnessTable(protoDecl)) { auto descriptor = emitProtocolDescriptorRef(IGF, protoDecl); witnessTableProtos.push_back(descriptor); } if (protoDecl->isObjC()) objcProtos.push_back(emitReferenceToObjCProtocol(IGF, protoDecl)); } llvm::Type *resultType; if (metatypeKind) { switch (*metatypeKind) { case MetatypeRepresentation::Thin: llvm_unreachable("can't cast to thin metatype"); case MetatypeRepresentation::Thick: resultType = IGF.IGM.TypeMetadataPtrTy; break; case MetatypeRepresentation::ObjC: resultType = IGF.IGM.ObjCClassPtrTy; break; } } else { auto schema = IGF.getTypeInfo(destType).getSchema(); resultType = schema[0].getScalarType(); } // The source of a scalar cast is statically known to be a class or a // metatype, so we only have to check the class constraint in two cases: // // 1) The destination type has an explicit superclass constraint that is // more derived than what the source type is known to be. // // 2) We are casting between metatypes, in which case the source might // be a non-class metatype. bool checkClassConstraint = false; if ((bool)metatypeKind && hasClassConstraint && !hasClassConstraintByProtocol && !srcInstanceType->mayHaveSuperclass()) checkClassConstraint = true; // If the source has an equal or more derived superclass constraint than // the destination, we can elide the superclass check. // // Note that destInstanceType is always an existential type, so calling // getSuperclass() returns the superclass constraint of the existential, // not the superclass of some concrete class. bool checkSuperclassConstraint = hasSuperclassConstraint && !destInstanceType->getSuperclass()->isExactSuperclassOf(srcInstanceType); if (checkSuperclassConstraint) checkClassConstraint = true; llvm::Value *resultValue = value; // If we don't have anything we really need to check, then trivially succeed. if (objcProtos.empty() && witnessTableProtos.empty() && !checkClassConstraint) { resultValue = IGF.Builder.CreateBitCast(value, resultType); ex.add(resultValue); return; } // Check the ObjC protocol conformances if there were any. llvm::Value *objcCast = nullptr; if (!objcProtos.empty()) { // Get the ObjC instance or class object to check for these conformances. llvm::Value *objcObject; if (metatypeKind) { switch (*metatypeKind) { case MetatypeRepresentation::Thin: llvm_unreachable("can't cast to thin metatype"); case MetatypeRepresentation::Thick: { // The metadata might be for a non-class type, which wouldn't have // an ObjC class object. objcObject = nullptr; break; } case MetatypeRepresentation::ObjC: // Metatype is already an ObjC object. objcObject = value; break; } } else { // Class instance is already an ObjC object. objcObject = value; } if (objcObject) objcObject = IGF.Builder.CreateBitCast(objcObject, IGF.IGM.UnknownRefCountedPtrTy); // Pick the cast function based on the cast mode and on whether we're // casting a Swift metatype or ObjC object. llvm::Constant *castFn; switch (mode) { case CheckedCastMode::Unconditional: castFn = objcObject ? IGF.IGM.getDynamicCastObjCProtocolUnconditionalFn() : IGF.IGM.getDynamicCastTypeToObjCProtocolUnconditionalFn(); break; case CheckedCastMode::Conditional: castFn = objcObject ? IGF.IGM.getDynamicCastObjCProtocolConditionalFn() : IGF.IGM.getDynamicCastTypeToObjCProtocolConditionalFn(); break; } llvm::Value *objcCastObject = objcObject ? objcObject : value; Address protoRefsBuf = IGF.createAlloca( llvm::ArrayType::get(IGF.IGM.Int8PtrTy, objcProtos.size()), IGF.IGM.getPointerAlignment(), "objc_protocols"); protoRefsBuf = IGF.Builder.CreateBitCast(protoRefsBuf, IGF.IGM.Int8PtrPtrTy); for (unsigned index : indices(objcProtos)) { Address protoRefSlot = IGF.Builder.CreateConstArrayGEP( protoRefsBuf, index, IGF.IGM.getPointerSize()); IGF.Builder.CreateStore(objcProtos[index], protoRefSlot); ++index; } auto cc = IGF.IGM.DefaultCC; if (auto fun = dyn_cast<llvm::Function>(castFn)) cc = fun->getCallingConv(); auto call = IGF.Builder.CreateCall( castFn, {objcCastObject, IGF.IGM.getSize(Size(objcProtos.size())), protoRefsBuf.getAddress()}); call->setCallingConv(cc); objcCast = call; resultValue = IGF.Builder.CreateBitCast(objcCast, resultType); } // If we don't need to look up any witness tables, we're done. if (witnessTableProtos.empty() && !checkClassConstraint) { ex.add(resultValue); return; } // If we're doing a conditional cast, and the ObjC protocol checks failed, // then the cast is done. Optional<ConditionalDominanceScope> condition; llvm::BasicBlock *origBB = nullptr, *successBB = nullptr, *contBB = nullptr; if (!objcProtos.empty()) { switch (mode) { case CheckedCastMode::Unconditional: break; case CheckedCastMode::Conditional: { origBB = IGF.Builder.GetInsertBlock(); successBB = IGF.createBasicBlock("success"); contBB = IGF.createBasicBlock("cont"); auto isNull = IGF.Builder.CreateICmpEQ(objcCast, llvm::ConstantPointerNull::get( cast<llvm::PointerType>(objcCast->getType()))); IGF.Builder.CreateCondBr(isNull, contBB, successBB); IGF.Builder.emitBlock(successBB); condition.emplace(IGF); } } } // Get the Swift type metadata for the type. llvm::Value *metadataValue; if (metatypeKind) { switch (*metatypeKind) { case MetatypeRepresentation::Thin: llvm_unreachable("can't cast to thin metatype"); case MetatypeRepresentation::Thick: // The value is already a native metatype. metadataValue = value; break; case MetatypeRepresentation::ObjC: // Get the type metadata from the ObjC class, which may be a wrapper. metadataValue = emitObjCMetadataRefForMetadata(IGF, value); } } else { // Get the type metadata for the instance. metadataValue = emitDynamicTypeOfHeapObject(IGF, value, srcType); } // Look up witness tables for the protocols that need them. auto fn = emitExistentialScalarCastFn(IGF.IGM, witnessTableProtos.size(), mode, checkClassConstraint, checkSuperclassConstraint); llvm::SmallVector<llvm::Value *, 4> args; if (resultValue->getType() != IGF.IGM.Int8PtrTy) resultValue = IGF.Builder.CreateBitCast(resultValue, IGF.IGM.Int8PtrTy); args.push_back(resultValue); args.push_back(metadataValue); if (checkSuperclassConstraint) args.push_back(IGF.emitTypeMetadataRef(CanType(layout.superclass))); for (auto proto : witnessTableProtos) args.push_back(proto); auto valueAndWitnessTables = IGF.Builder.CreateCall(fn, args); resultValue = IGF.Builder.CreateExtractValue(valueAndWitnessTables, 0); if (resultValue->getType() != resultType) resultValue = IGF.Builder.CreateBitCast(resultValue, resultType); ex.add(resultValue); for (unsigned i = 0, e = witnessTableProtos.size(); i < e; ++i) { auto wt = IGF.Builder.CreateExtractValue(valueAndWitnessTables, i + 1); ex.add(wt); } // If we had conditional ObjC checks, join the failure paths. if (contBB) { condition.reset(); IGF.Builder.CreateBr(contBB); IGF.Builder.emitBlock(contBB); // Return null on the failure path. Explosion successEx = std::move(ex); ex.reset(); while (!successEx.empty()) { auto successVal = successEx.claimNext(); auto failureVal = llvm::Constant::getNullValue(successVal->getType()); auto phi = IGF.Builder.CreatePHI(successVal->getType(), 2); phi->addIncoming(successVal, successBB); phi->addIncoming(failureVal, origBB); ex.add(phi); } } }