static DynamicCastFeasibility classifyDynamicCastFromProtocol(ModuleDecl *M, CanType source, CanType target, bool isWholeModuleOpts) { assert(source.isExistentialType() && "source should be an existential type"); if (source == target) return DynamicCastFeasibility::WillSucceed; // Casts from class existential into a non-class can never succeed. if (source->isClassExistentialType() && !target.isAnyExistentialType() && !target.getClassOrBoundGenericClass() && !isa<ArchetypeType>(target) && !mayBridgeToObjectiveC(M, target)) { assert((target.getEnumOrBoundGenericEnum() || target.getStructOrBoundGenericStruct() || isa<TupleType>(target) || isa<SILFunctionType>(target) || isa<FunctionType>(target) || isa<MetatypeType>(target)) && "Target should be an enum, struct, tuple, metatype or function type"); return DynamicCastFeasibility::WillFail; } // TODO: maybe prove that certain conformances are impossible? return DynamicCastFeasibility::MaySucceed; }
/// Build an interface type substitution map for the given generic signature /// from a type substitution function and conformance lookup function. SubstitutionMap SubstitutionMap::get(GenericSignature *genericSig, TypeSubstitutionFn subs, LookupConformanceFn lookupConformance) { if (!genericSig) { return SubstitutionMap(); } // Form the replacement types. SmallVector<Type, 4> replacementTypes; replacementTypes.reserve(genericSig->getGenericParams().size()); for (auto gp : genericSig->getGenericParams()) { // Don't eagerly form replacements for non-canonical generic parameters. if (!genericSig->isCanonicalTypeInContext(gp->getCanonicalType())) { replacementTypes.push_back(Type()); continue; } // Record the replacement. Type replacement = Type(gp).subst(subs, lookupConformance, SubstFlags::UseErrorType); replacementTypes.push_back(replacement); } // Form the stored conformances. SmallVector<ProtocolConformanceRef, 4> conformances; for (const auto &req : genericSig->getRequirements()) { if (req.getKind() != RequirementKind::Conformance) continue; CanType depTy = req.getFirstType()->getCanonicalType(); auto replacement = depTy.subst(subs, lookupConformance, SubstFlags::UseErrorType); auto protoType = req.getSecondType()->castTo<ProtocolType>(); auto proto = protoType->getDecl(); auto conformance = lookupConformance(depTy, replacement, proto) .getValueOr(ProtocolConformanceRef(proto)); conformances.push_back(conformance); } return SubstitutionMap(genericSig, replacementTypes, conformances); }
// Collect any builtin types referenced from this type. void addBuiltinTypeRefs(CanType type) { type.visit([&](Type t) { if (t->is<BuiltinType>()) BuiltinTypes.insert(CanType(t)); // We need at least size/alignment information for types imported by // clang, so we'll treat them as a builtin type, essentially an // opaque blob. if (auto Nominal = t->getAnyNominal()) if (Nominal->hasClangNode()) BuiltinTypes.insert(CanType(t)); }); }
/// Perform a foreign error check by testing whether the call result is zero. /// The call result is otherwise ignored. static void emitResultIsZeroErrorCheck(SILGenFunction &gen, SILLocation loc, ManagedValue result, ManagedValue errorSlot, bool suppressErrorCheck, bool zeroIsError) { // Just ignore the call result if we're suppressing the error check. if (suppressErrorCheck) { return; } SILValue resultValue = emitUnwrapIntegerResult(gen, loc, result.getUnmanagedValue()); CanType resultType = resultValue->getType().getSwiftRValueType(); if (!resultType->isBuiltinIntegerType(1)) { SILValue zero = gen.B.createIntegerLiteral(loc, resultValue->getType(), 0); ASTContext &ctx = gen.getASTContext(); resultValue = gen.B.createBuiltinBinaryFunction(loc, "cmp_ne", resultValue->getType(), SILType::getBuiltinIntegerType(1, ctx), {resultValue, zero}); } SILBasicBlock *errorBB = gen.createBasicBlock(FunctionSection::Postmatter); SILBasicBlock *contBB = gen.createBasicBlock(); if (zeroIsError) gen.B.createCondBranch(loc, resultValue, contBB, errorBB); else gen.B.createCondBranch(loc, resultValue, errorBB, contBB); gen.emitForeignErrorBlock(loc, errorBB, errorSlot); gen.B.emitBlock(contBB); }
FormalLinkage swift::getTypeLinkage(CanType type) { FormalLinkage result = FormalLinkage::Top; // Merge all nominal types from the structural type. (void) type.findIf([&](Type _type) { CanType type = CanType(_type); // For any nominal type reference, look at the type declaration. if (auto nominal = type->getAnyNominal()) { result ^= getDeclLinkage(nominal); // For polymorphic function types, look at the generic parameters. // FIXME: findIf should do this, once polymorphic function types can be // canonicalized and re-formed properly. } else if (auto polyFn = dyn_cast<PolymorphicFunctionType>(type)) { result ^= getGenericClauseLinkage(polyFn->getGenericParameters()); } return false; // continue searching }); return result; }
/// Return a value for an optional ".Some(x)" of the specified type. This only /// works for loadable enum types. ManagedValue SILGenFunction:: getOptionalSomeValue(SILLocation loc, ManagedValue value, const TypeLowering &optTL) { assert(optTL.isLoadable() && "Address-only optionals cannot use this"); SILType optType = optTL.getLoweredType(); CanType formalOptType = optType.getSwiftRValueType(); OptionalTypeKind OTK; auto formalObjectType = formalOptType->getAnyOptionalObjectType(OTK) ->getCanonicalType(); assert(OTK != OTK_None); auto someDecl = getASTContext().getOptionalSomeDecl(OTK); AbstractionPattern origType = AbstractionPattern::getOpaque(); // Reabstract input value to the type expected by the enum. value = emitSubstToOrigValue(loc, value, origType, formalObjectType); SILValue result = B.createEnum(loc, value.forward(*this), someDecl, optTL.getLoweredType()); return emitManagedRValueWithCleanup(result, optTL); }
bool DestructorAnalysis::isSafeType(CanType Ty) { // Don't visit types twice. auto CachedRes = Cached.find(Ty); if (CachedRes != Cached.end()) { return CachedRes->second; } // Before we recurse mark the type as safe i.e if we see it in a recursive // position it is safe in the absence of another fact that proves otherwise. // We will reset this value to the correct value once we return from the // recursion below. cacheResult(Ty, true); // Trivial value types. if (Ty->is<BuiltinIntegerType>()) return cacheResult(Ty, true); if (Ty->is<BuiltinFloatType>()) return cacheResult(Ty, true); // A struct is safe if // * either it implements the _DestructorSafeContainer protocol and // all the type parameters are safe types. // * or all stored properties are safe types. if (auto *Struct = Ty->getStructOrBoundGenericStruct()) { if (implementsDestructorSafeContainerProtocol(Struct) && areTypeParametersSafe(Ty)) return cacheResult(Ty, true); // Check the stored properties. for (auto SP : Struct->getStoredProperties()) if (!isSafeType(SP->getInterfaceType()->getCanonicalType())) return cacheResult(Ty, false); return cacheResult(Ty, true); } // A tuple type is safe if its elements are safe. if (auto Tuple = dyn_cast<TupleType>(Ty)) { for (auto &Elt : Tuple->getElements()) if (!isSafeType(Elt.getType()->getCanonicalType())) return cacheResult(Ty, false); return cacheResult(Ty, true); } // TODO: enum types. return cacheResult(Ty, false); }
CanType swift::getNSBridgedClassOfCFClass(ModuleDecl *M, CanType type) { if (auto classDecl = type->getClassOrBoundGenericClass()) { if (classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType) { if (auto bridgedAttr = classDecl->getAttrs().getAttribute<ObjCBridgedAttr>()) { auto bridgedClass = bridgedAttr->getObjCClass(); // TODO: this should handle generic classes properly. if (!bridgedClass->isGenericContext()) { return bridgedClass->getDeclaredInterfaceType()->getCanonicalType(); } } } } return CanType(); }
/// Add a 32-bit relative offset to a mangled typeref string /// in the typeref reflection section. void addTypeRef(Module *ModuleContext, CanType type) { assert(type); // Generic parameters should be written in terms of interface types // for the purposes of reflection metadata assert(!type->hasArchetype() && "Forgot to map typeref out of context"); Mangle::Mangler mangler(/*DWARFMangling*/false, /*usePunyCode*/ true, /*OptimizeProtocolNames*/ false); mangler.setModuleContext(ModuleContext); mangler.mangleType(type, 0); auto mangledName = IGM.getAddrOfStringForTypeRef(mangler.finalize()); addRelativeAddress(mangledName); }
CanType TypeConverter::getBridgedInputType(SILFunctionTypeRepresentation rep, AbstractionPattern pattern, CanType input) { if (auto tuple = dyn_cast<TupleType>(input)) { SmallVector<TupleTypeElt, 4> bridgedFields; bool changed = false; for (unsigned i : indices(tuple->getElements())) { auto &elt = tuple->getElement(i); Type bridged = getLoweredBridgedType(pattern.getTupleElementType(i), elt.getType(), rep, TypeConverter::ForArgument); if (!bridged) { Context.Diags.diagnose(SourceLoc(), diag::could_not_find_bridge_type, elt.getType()); llvm::report_fatal_error("unable to set up the ObjC bridge!"); } CanType canBridged = bridged->getCanonicalType(); if (canBridged != CanType(elt.getType())) { changed = true; bridgedFields.push_back(elt.getWithType(canBridged)); } else { bridgedFields.push_back(elt); } } if (!changed) return input; return CanType(TupleType::get(bridgedFields, input->getASTContext())); } auto loweredBridgedType = getLoweredBridgedType(pattern, input, rep, TypeConverter::ForArgument); if (!loweredBridgedType) { Context.Diags.diagnose(SourceLoc(), diag::could_not_find_bridge_type, input); llvm::report_fatal_error("unable to set up the ObjC bridge!"); } return loweredBridgedType->getCanonicalType(); }
Optional<ProtocolConformanceRef> SubstitutionMap::forEachParent(CanType type, Fn fn) const { auto foundParents = parentMap.find(type.getPointer()); if (foundParents != parentMap.end()) { for (auto parent : foundParents->second) { if (auto result = fn(parent.first, parent.second)) return result; } } if (auto archetypeType = dyn_cast<ArchetypeType>(type)) if (auto *parent = archetypeType->getParent()) return fn(CanType(parent), archetypeType->getAssocType()); if (auto memberType = dyn_cast<DependentMemberType>(type)) return fn(CanType(memberType->getBase()), memberType->getAssocType()); return None; }
// FIXME: With some changes to their callers, all of the below functions // could be re-worked to use emitInjectEnum(). ManagedValue SILGenFunction::emitInjectOptional(SILLocation loc, ManagedValue v, CanType inputFormalType, CanType substFormalType, const TypeLowering &expectedTL, SGFContext ctxt) { // Optional's payload is currently maximally abstracted. FIXME: Eventually // it shouldn't be. auto opaque = AbstractionPattern::getOpaque(); OptionalTypeKind substOTK; auto substObjectType = substFormalType.getAnyOptionalObjectType(substOTK); auto loweredTy = getLoweredType(opaque, substObjectType); if (v.getType() != loweredTy) v = emitTransformedValue(loc, v, AbstractionPattern(inputFormalType), inputFormalType, opaque, substObjectType); auto someDecl = getASTContext().getOptionalSomeDecl(substOTK); SILType optTy = getLoweredType(substFormalType); if (v.getType().isAddress()) { auto buf = getBufferForExprResult(loc, optTy.getObjectType(), ctxt); auto payload = B.createInitEnumDataAddr(loc, buf, someDecl, v.getType()); // FIXME: Is it correct to use IsTake here even if v doesn't have a cleanup? B.createCopyAddr(loc, v.forward(*this), payload, IsTake, IsInitialization); B.createInjectEnumAddr(loc, buf, someDecl); v = manageBufferForExprResult(buf, expectedTL, ctxt); } else { auto some = B.createEnum(loc, v.getValue(), someDecl, optTy); v = ManagedValue(some, v.getCleanup()); } return v; }
/// Add a 32-bit relative offset to a mangled typeref string /// in the typeref reflection section. void addTypeRef(ModuleDecl *ModuleContext, CanType type, CanGenericSignature Context = {}) { assert(type); // Generic parameters should be written in terms of interface types // for the purposes of reflection metadata assert(!type->hasArchetype() && "Forgot to map typeref out of context"); // TODO: As a compatibility hack, mangle single-field boxes with the legacy // mangling in reflection metadata. bool isSingleFieldOfBox = false; auto boxTy = dyn_cast<SILBoxType>(type); if (boxTy && boxTy->getLayout()->getFields().size() == 1) { GenericContextScope scope(IGM, Context); type = boxTy->getFieldLoweredType(IGM.getSILModule(), 0); isSingleFieldOfBox = true; } IRGenMangler mangler; std::string MangledStr = mangler.mangleTypeForReflection(type, ModuleContext, isSingleFieldOfBox); auto mangledName = IGM.getAddrOfStringForTypeRef(MangledStr); addRelativeAddress(mangledName); }
// Collect any builtin types referenced from this type. void addBuiltinTypeRefs(CanType type) { type.visit([&](Type t) { if (t->is<BuiltinType>()) IGM.BuiltinTypes.insert(CanType(t)); // We need size/alignment information for imported value types, // so emit builtin descriptors for them. // // In effect they're treated like an opaque blob, which is OK // for now, at least until we want to import C++ types or // something like that. // // Classes and protocols go down a different path. if (auto Nominal = t->getAnyNominal()) if (Nominal->hasClangNode()) { if (auto CD = dyn_cast<ClassDecl>(Nominal)) IGM.ImportedClasses.insert(CD); else if (auto PD = dyn_cast<ProtocolDecl>(Nominal)) IGM.ImportedProtocols.insert(PD); else IGM.BuiltinTypes.insert(CanType(t)); } }); }
/// Is metadata for the given type kind a "leaf", or does it possibly /// store any other type metadata that we can statically extract? /// /// It's okay to conservatively answer "no". It's more important for this /// to be quick than for it to be accurate; don't recurse. static bool isLeafTypeMetadata(CanType type) { switch (type->getKind()) { #define SUGARED_TYPE(ID, SUPER) \ case TypeKind::ID: #define UNCHECKED_TYPE(ID, SUPER) \ case TypeKind::ID: #define TYPE(ID, SUPER) #include "swift/AST/TypeNodes.def" llvm_unreachable("kind is invalid for a canonical type"); #define ARTIFICIAL_TYPE(ID, SUPER) \ case TypeKind::ID: #define TYPE(ID, SUPER) #include "swift/AST/TypeNodes.def" case TypeKind::LValue: case TypeKind::InOut: case TypeKind::DynamicSelf: llvm_unreachable("these types do not have metadata"); // All the builtin types are leaves. #define BUILTIN_TYPE(ID, SUPER) \ case TypeKind::ID: #define TYPE(ID, SUPER) #include "swift/AST/TypeNodes.def" case TypeKind::Module: return true; // Type parameters are statically opaque. case TypeKind::Archetype: case TypeKind::GenericTypeParam: case TypeKind::DependentMember: return true; // Only the empty tuple is a leaf. case TypeKind::Tuple: return cast<TupleType>(type)->getNumElements() == 0; // Nominal types might have parents. case TypeKind::Class: case TypeKind::Enum: case TypeKind::Protocol: case TypeKind::Struct: return !cast<NominalType>(type)->getParent(); // Bound generic types have type arguments. case TypeKind::BoundGenericClass: case TypeKind::BoundGenericEnum: case TypeKind::BoundGenericStruct: return false; // Functions have component types. case TypeKind::Function: case TypeKind::PolymorphicFunction: case TypeKind::GenericFunction: // included for future-proofing return false; // Protocol compositions have component types. case TypeKind::ProtocolComposition: return false; // Metatypes have instance types. case TypeKind::Metatype: case TypeKind::ExistentialMetatype: return false; } llvm_unreachable("bad type kind"); }
// Start with the substitutions from the apply. // Try to propagate them to find out the real substitutions required // to invoke the method. static ArrayRef<Substitution> getSubstitutionsForCallee(SILModule &M, CanSILFunctionType GenCalleeType, SILType ClassInstanceType, FullApplySite AI) { // *NOTE*: // Apply instruction substitutions are for the Member from a protocol or // class B, where this member was first defined, before it got overridden by // derived classes. // // The implementation F (the implementing method) which was found may have // a different set of generic parameters, e.g. because it is implemented by a // class D1 derived from B. // // ClassInstanceType may have a type different from both the type B // the Member belongs to and from the ClassInstanceType, e.g. if // ClassInstance is of a class D2, which is derived from D1, but does not // override the Member. // // As a result, substitutions provided by AI are for Member, whereas // substitutions in ClassInstanceType are for D2. And substitutions for D1 // are not available directly in a general case. Therefore, they have to // be computed. // // What we know for sure: // B is a superclass of D1 // D1 is a superclass of D2. // D1 can be the same as D2. D1 can be the same as B. // // So, substitutions from AI are for class B. // Substitutions for class D1 by means of bindSuperclass(), which starts // with a bound type ClassInstanceType and checks its superclasses until it // finds a bound superclass matching D1 and returns its substitutions. // Class F belongs to. CanType FSelfClass = GenCalleeType->getSelfParameter().getType(); SILType FSelfSubstType; Module *Module = M.getSwiftModule(); ArrayRef<Substitution> ClassSubs; if (GenCalleeType->isPolymorphic()) { // Declaration of the class F belongs to. if (auto *FSelfTypeDecl = FSelfClass.getNominalOrBoundGenericNominal()) { // Get the unbound generic type F belongs to. CanType FSelfGenericType = FSelfTypeDecl->getDeclaredType()->getCanonicalType(); assert((isa<BoundGenericType>(ClassInstanceType.getSwiftRValueType()) || isa<NominalType>(ClassInstanceType.getSwiftRValueType())) && "Self type should be either a bound generic type" "or a non-generic type"); assert((isa<UnboundGenericType>(FSelfGenericType) || isa<NominalType>(FSelfGenericType)) && "Method implementation self type should be generic"); if (isa<BoundGenericType>(ClassInstanceType.getSwiftRValueType())) { auto BoundBaseType = bindSuperclass(FSelfGenericType, ClassInstanceType); if (auto BoundTy = BoundBaseType->getAs<BoundGenericType>()) { ClassSubs = BoundTy->getSubstitutions(Module, nullptr); } } } } else { // If the callee is not polymorphic, no substitutions are required. return {}; } if (ClassSubs.empty()) return AI.getSubstitutions(); auto AISubs = AI.getSubstitutions(); CanSILFunctionType AIGenCalleeType = AI.getCallee().getType().castTo<SILFunctionType>(); CanType AISelfClass = AIGenCalleeType->getSelfParameter().getType(); unsigned NextMethodParamIdx = 0; unsigned NumMethodParams = 0; if (AIGenCalleeType->isPolymorphic()) { NextMethodParamIdx = 0; // Generic parameters of the method start after generic parameters // of the instance class. if (auto AISelfClassSig = AISelfClass.getClassBound()->getGenericSignature()) { NextMethodParamIdx = AISelfClassSig->getGenericParams().size(); } NumMethodParams = AISubs.size() - NextMethodParamIdx; } unsigned NumSubs = ClassSubs.size() + NumMethodParams; if (ClassSubs.size() == NumSubs) return ClassSubs; // Mix class subs with method specific subs from the AI substitutions. // Assumptions: AI substitutions contain first the substitutions for // a class of the method being invoked and then the substitutions // for a method being invoked. auto Subs = M.getASTContext().Allocate<Substitution>(NumSubs); unsigned i = 0; for (auto &S : ClassSubs) { Subs[i++] = S; } for (; i < NumSubs; ++i, ++NextMethodParamIdx) { Subs[i] = AISubs[NextMethodParamIdx]; } return Subs; }
void LocalTypeDataCache:: addAbstractForFulfillments(IRGenFunction &IGF, FulfillmentMap &&fulfillments, llvm::function_ref<AbstractSource()> createSource) { // Add the source lazily. Optional<unsigned> sourceIndex; auto getSourceIndex = [&]() -> unsigned { if (!sourceIndex) { AbstractSources.emplace_back(createSource()); sourceIndex = AbstractSources.size() - 1; } return *sourceIndex; }; for (auto &fulfillment : fulfillments) { CanType type = CanType(fulfillment.first.first); LocalTypeDataKind localDataKind; // For now, ignore witness-table fulfillments when they're not for // archetypes. if (ProtocolDecl *protocol = fulfillment.first.second) { if (auto archetype = dyn_cast<ArchetypeType>(type)) { auto conformsTo = archetype->getConformsTo(); auto it = std::find(conformsTo.begin(), conformsTo.end(), protocol); if (it == conformsTo.end()) continue; localDataKind = LocalTypeDataKind::forAbstractProtocolWitnessTable(*it); } else { continue; } } else { // Ignore type metadata fulfillments for non-dependent types that // we can produce very cheaply. We don't want to end up emitting // the type metadata for Int by chasing through N layers of metadata // just because that path happens to be in the cache. if (!type->hasArchetype() && isTypeMetadataAccessTrivial(IGF.IGM, type)) { continue; } localDataKind = LocalTypeDataKind::forTypeMetadata(); } // Find the chain for the key. auto key = getKey(type, localDataKind); auto &chain = Map[key]; // Check whether there's already an entry that's at least as good as the // fulfillment. Optional<unsigned> fulfillmentCost; auto getFulfillmentCost = [&]() -> unsigned { if (!fulfillmentCost) fulfillmentCost = fulfillment.second.Path.cost(); return *fulfillmentCost; }; bool isConditional = IGF.isConditionalDominancePoint(); bool foundBetter = false; for (CacheEntry *cur = chain.Root, *last = nullptr; cur; last = cur, cur = cur->getNext()) { // Ensure the entry is acceptable. if (!IGF.isActiveDominancePointDominatedBy(cur->DefinitionPoint)) continue; // Ensure that the entry isn't better than the fulfillment. auto curCost = cur->cost(); if (curCost == 0 || curCost <= getFulfillmentCost()) { foundBetter = true; break; } // If the entry is defined at the current point, (1) we know there // won't be a better entry and (2) we should remove it. if (cur->DefinitionPoint == IGF.getActiveDominancePoint() && !isConditional) { // Splice it out of the chain. assert(!cur->isConditional()); chain.eraseEntry(last, cur); break; } } if (foundBetter) continue; // Okay, make a new entry. // Register with the conditional dominance scope if necessary. if (isConditional) { IGF.registerConditionalLocalTypeDataKey(key); } // Allocate the new entry. auto newEntry = new AbstractCacheEntry(IGF.getActiveDominancePoint(), isConditional, getSourceIndex(), std::move(fulfillment.second.Path)); // Add it to the front of the chain. chain.push_front(newEntry); } }
/// Emit a single protocol witness table reference. llvm::Value *irgen::emitArchetypeWitnessTableRef(IRGenFunction &IGF, CanArchetypeType archetype, ProtocolDecl *protocol) { assert(Lowering::TypeConverter::protocolRequiresWitnessTable(protocol) && "looking up witness table for protocol that doesn't have one"); // The following approach assumes that a protocol will only appear in // an archetype's conformsTo array if the archetype is either explicitly // constrained to conform to that protocol (in which case we should have // a cache entry for it) or there's an associated type declaration with // that protocol listed as a direct requirement. auto localDataKind = LocalTypeDataKind::forAbstractProtocolWitnessTable(protocol); // Check immediately for an existing cache entry. // TODO: don't give this absolute precedence over other access paths. auto wtable = IGF.tryGetLocalTypeData(archetype, localDataKind); if (wtable) return wtable; // If we don't have an environment, this must be an implied witness table // reference. // FIXME: eliminate this path when opened types have generic environments. auto environment = archetype->getGenericEnvironment(); if (!environment) { assert(archetype->isOpenedExistential() && "non-opened archetype lacking generic environment?"); SmallVector<ProtocolEntry, 4> entries; for (auto p : archetype->getConformsTo()) { const ProtocolInfo &impl = IGF.IGM.getProtocolInfo(p, ProtocolInfoKind::RequirementSignature); entries.push_back(ProtocolEntry(p, impl)); } return emitImpliedWitnessTableRef(IGF, entries, protocol, [&](unsigned index) -> llvm::Value* { auto localDataKind = LocalTypeDataKind::forAbstractProtocolWitnessTable( entries[index].getProtocol()); auto wtable = IGF.tryGetLocalTypeData(archetype, localDataKind); assert(wtable && "opened type without local type data for direct conformance?"); return wtable; }); } // Otherwise, ask the generic signature for the environment for the best // path to the conformance. // TODO: this isn't necessarily optimal if the direct conformance isn't // concretely available; we really ought to be comparing the full paths // to this conformance from concrete sources. auto signature = environment->getGenericSignature()->getCanonicalSignature(); auto archetypeDepType = archetype->getInterfaceType(); auto astPath = signature->getConformanceAccessPath(archetypeDepType, protocol); auto i = astPath.begin(), e = astPath.end(); assert(i != e && "empty path!"); // The first entry in the path is a direct requirement of the signature, // for which we should always have local type data available. CanType rootArchetype = environment->mapTypeIntoContext(i->first)->getCanonicalType(); ProtocolDecl *rootProtocol = i->second; // Turn the rest of the path into a MetadataPath. auto lastProtocol = rootProtocol; MetadataPath path; while (++i != e) { auto &entry = *i; CanType depType = CanType(entry.first); ProtocolDecl *requirement = entry.second; const ProtocolInfo &lastPI = IGF.IGM.getProtocolInfo(lastProtocol, ProtocolInfoKind::RequirementSignature); // If it's a type parameter, it's self, and this is a base protocol // requirement. if (isa<GenericTypeParamType>(depType)) { assert(depType->isEqual(lastProtocol->getSelfInterfaceType())); WitnessIndex index = lastPI.getBaseIndex(requirement); path.addInheritedProtocolComponent(index); // Otherwise, it's an associated conformance requirement. } else { AssociatedConformance association(lastProtocol, depType, requirement); WitnessIndex index = lastPI.getAssociatedConformanceIndex(association); path.addAssociatedConformanceComponent(index); } lastProtocol = requirement; } assert(lastProtocol == protocol); auto rootWTable = IGF.tryGetLocalTypeData(rootArchetype, LocalTypeDataKind::forAbstractProtocolWitnessTable(rootProtocol)); assert(rootWTable && "root witness table not bound in local context!"); wtable = path.followFromWitnessTable(IGF, rootArchetype, ProtocolConformanceRef(rootProtocol), MetadataResponse::forComplete(rootWTable), /*request*/ MetadataState::Complete, nullptr).getMetadata(); return wtable; }
Optional<T> SubstitutionMap::forEachConformance( CanType type, llvm::SmallPtrSetImpl<CanType> &visitedParents, llvm::function_ref<Optional<T>(ProtocolConformanceRef)> fn) const{ // Check for conformances for the type that apply to the original // substituted archetype. auto foundReplacement = conformanceMap.find(type.getPointer()); if (foundReplacement != conformanceMap.end()) { for (auto conformance : foundReplacement->second) { if (auto found = fn(conformance)) return found; } } // Local function to performance a (recursive) search for an associated type // of the given name in the given conformance and all inherited conformances. std::function<Optional<T>(ProtocolConformanceRef, DeclName, llvm::SmallPtrSetImpl<ProtocolDecl *> &)> searchInConformance; searchInConformance = [&](ProtocolConformanceRef conformance, DeclName associatedTypeName, llvm::SmallPtrSetImpl<ProtocolDecl *> &visited) -> Optional<T> { // Only visit a particular protocol once. auto proto = conformance.getRequirement(); if (!visited.insert(proto).second) return None; // Check whether this protocol has an associated type with the // same name as the one we're looking for. AssociatedTypeDecl *protoAssocType = nullptr; for (auto member : proto->lookupDirect(associatedTypeName)) { protoAssocType = dyn_cast<AssociatedTypeDecl>(member); if (protoAssocType) break; } if (protoAssocType) { if (conformance.isAbstract()) { for (auto assocProto : protoAssocType->getConformingProtocols()) { if (auto found = fn(ProtocolConformanceRef(assocProto))) return found; } } else { auto sub = conformance.getConcrete()->getTypeWitnessSubstAndDecl( protoAssocType, nullptr).first; for (auto subConformance : sub.getConformances()) { if (auto found = fn(subConformance)) return found; } } } // Search inherited conformances. for (auto inherited : proto->getInheritedProtocols(nullptr)) { if (auto found = searchInConformance(conformance.getInherited(inherited), associatedTypeName, visited)) return found; } return None; }; // Check if we have conformances from one of our parent types. return forEachParent<ProtocolConformanceRef>(type, visitedParents, [&](CanType parent, AssociatedTypeDecl *assocType) -> Optional<ProtocolConformanceRef> { return forEachConformance<T>(parent, visitedParents, [&](ProtocolConformanceRef conformance) -> Optional<T> { llvm::SmallPtrSet<ProtocolDecl *, 4> visited; return searchInConformance(conformance, assocType->getFullName(), visited); }); }); }
void SubstitutionMap:: addParent(CanType type, CanType parent, AssociatedTypeDecl *assocType) { assert(type && parent && assocType); parentMap[type.getPointer()].push_back(std::make_pair(parent, assocType)); }
ArrayRef<ProtocolConformanceRef> SubstitutionMap:: getConformances(CanType type) const { auto known = conformanceMap.find(type.getPointer()); if (known == conformanceMap.end()) return { }; return known->second; }
/// Given that a type is not statically known to be an optional type, check whether /// it might dynamically be an optional type. static bool canDynamicallyBeOptionalType(CanType type) { assert(!type.getAnyOptionalObjectType()); return (isa<ArchetypeType>(type) || type.isExistentialType()) && !type.isAnyClassReferenceType(); }
ManagedValue SILGenFunction::emitExistentialErasure( SILLocation loc, CanType concreteFormalType, const TypeLowering &concreteTL, const TypeLowering &existentialTL, ArrayRef<ProtocolConformanceRef> conformances, SGFContext C, llvm::function_ref<ManagedValue (SGFContext)> F, bool allowEmbeddedNSError) { // Mark the needed conformances as used. for (auto conformance : conformances) SGM.useConformance(conformance); // If we're erasing to the 'Error' type, we might be able to get an NSError // representation more efficiently. auto &ctx = getASTContext(); auto nsError = ctx.getNSErrorDecl(); if (allowEmbeddedNSError && nsError && existentialTL.getSemanticType().getSwiftRValueType()->getAnyNominal() == ctx.getErrorDecl()) { // Check whether the concrete type conforms to the _BridgedStoredNSError // protocol. In that case, call the _nsError witness getter to extract the // NSError directly. auto conformance = SGM.getConformanceToBridgedStoredNSError(loc, concreteFormalType); CanType nsErrorType = nsError->getDeclaredInterfaceType()->getCanonicalType(); ProtocolConformanceRef nsErrorConformances[1] = { ProtocolConformanceRef(SGM.getNSErrorConformanceToError()) }; if (conformance && nsError && SGM.getNSErrorConformanceToError()) { if (auto witness = conformance->getWitness(SGM.getNSErrorRequirement(loc), nullptr)) { // Create a reference to the getter witness. SILDeclRef getter = getGetterDeclRef(cast<VarDecl>(witness.getDecl()), /*isDirectAccessorUse=*/true); // Compute the substitutions. ArrayRef<Substitution> substitutions = concreteFormalType->gatherAllSubstitutions( SGM.SwiftModule, nullptr); // Emit the erasure, through the getter to _nsError. return emitExistentialErasure( loc, nsErrorType, getTypeLowering(nsErrorType), existentialTL, ctx.AllocateCopy(nsErrorConformances), C, [&](SGFContext innerC) -> ManagedValue { // Call the getter. return emitGetAccessor(loc, getter, substitutions, ArgumentSource(loc, RValue(*this, loc, concreteFormalType, F(SGFContext()))), /*isSuper=*/false, /*isDirectAccessorUse=*/true, RValue(), innerC) .getAsSingleValue(*this, loc); }); } } // Check whether the concrete type is an archetype. If so, call the // _getEmbeddedNSError() witness to try to dig out the embedded NSError. if (auto archetypeType = concreteFormalType->getAs<ArchetypeType>()) { if (std::find(archetypeType->getConformsTo().begin(), archetypeType->getConformsTo().end(), ctx.getErrorDecl()) != archetypeType->getConformsTo().end()) { auto contBB = createBasicBlock(); auto isNotPresentBB = createBasicBlock(); auto isPresentBB = createBasicBlock(); SILValue existentialResult = contBB->createBBArg(existentialTL.getLoweredType()); ProtocolConformanceRef trivialErrorConformances[1] = { ProtocolConformanceRef(ctx.getErrorDecl()) }; Substitution substitutions[1] = { Substitution(concreteFormalType, ctx.AllocateCopy(trivialErrorConformances)) }; // Call swift_stdlib_getErrorEmbeddedNSError to attempt to extract an // NSError from the value. ManagedValue concreteValue = F(SGFContext()); ManagedValue potentialNSError = emitApplyOfLibraryIntrinsic(loc, SGM.getGetErrorEmbeddedNSError(loc), ctx.AllocateCopy(substitutions), { concreteValue }, SGFContext()) .getAsSingleValue(*this, loc); // Check whether we got an NSError back. SILValue hasNSError = emitDoesOptionalHaveValue(loc, potentialNSError.getValue()); B.createCondBranch(loc, hasNSError, isPresentBB, isNotPresentBB); // If we did get an NSError, emit the existential erasure from that // NSError. B.emitBlock(isPresentBB); SILValue branchArg; { // Don't allow cleanups to escape the conditional block. FullExpr presentScope(Cleanups, CleanupLocation::get(loc)); // Emit the existential erasure from the NSError. branchArg = emitExistentialErasure( loc, nsErrorType, getTypeLowering(nsErrorType), existentialTL, ctx.AllocateCopy(nsErrorConformances), C, [&](SGFContext innerC) -> ManagedValue { // Pull the NSError object out of the optional result. auto &inputTL = getTypeLowering(potentialNSError.getType()); auto nsErrorValue = emitUncheckedGetOptionalValueFrom(loc, potentialNSError, inputTL); // Perform an unchecked cast down to NSError, because it was typed // as 'AnyObject' for layering reasons. return ManagedValue(B.createUncheckedRefCast( loc, nsErrorValue.getValue(), getLoweredType(nsErrorType)), nsErrorValue.getCleanup()); }).forward(*this); } B.createBranch(loc, contBB, branchArg); // If we did not get an NSError, just directly emit the existential // (recursively). B.emitBlock(isNotPresentBB); branchArg = emitExistentialErasure(loc, concreteFormalType, concreteTL, existentialTL, conformances, SGFContext(), F, /*allowEmbeddedNSError=*/false) .forward(*this); B.createBranch(loc, contBB, branchArg); // Continue. B.emitBlock(contBB); return emitManagedRValueWithCleanup(existentialResult, existentialTL); } } } switch (existentialTL.getLoweredType().getObjectType() .getPreferredExistentialRepresentation(SGM.M, concreteFormalType)) { case ExistentialRepresentation::None: llvm_unreachable("not an existential type"); case ExistentialRepresentation::Metatype: { assert(existentialTL.isLoadable()); SILValue metatype = F(SGFContext()).getUnmanagedValue(); assert(metatype->getType().castTo<AnyMetatypeType>()->getRepresentation() == MetatypeRepresentation::Thick); auto upcast = B.createInitExistentialMetatype(loc, metatype, existentialTL.getLoweredType(), conformances); return ManagedValue::forUnmanaged(upcast); } case ExistentialRepresentation::Class: { assert(existentialTL.isLoadable()); ManagedValue sub = F(SGFContext()); SILValue v = B.createInitExistentialRef(loc, existentialTL.getLoweredType(), concreteFormalType, sub.getValue(), conformances); return ManagedValue(v, sub.getCleanup()); } case ExistentialRepresentation::Boxed: { // Allocate the existential. auto *existential = B.createAllocExistentialBox(loc, existentialTL.getLoweredType(), concreteFormalType, conformances); auto *valueAddr = B.createProjectExistentialBox(loc, concreteTL.getLoweredType(), existential); // Initialize the concrete value in-place. InitializationPtr init( new ExistentialInitialization(existential, valueAddr, concreteFormalType, ExistentialRepresentation::Boxed, *this)); ManagedValue mv = F(SGFContext(init.get())); if (!mv.isInContext()) { mv.forwardInto(*this, loc, init->getAddress()); init->finishInitialization(*this); } return emitManagedRValueWithCleanup(existential); } case ExistentialRepresentation::Opaque: { // Allocate the existential. SILValue existential = getBufferForExprResult(loc, existentialTL.getLoweredType(), C); // Allocate the concrete value inside the container. SILValue valueAddr = B.createInitExistentialAddr( loc, existential, concreteFormalType, concreteTL.getLoweredType(), conformances); // Initialize the concrete value in-place. InitializationPtr init( new ExistentialInitialization(existential, valueAddr, concreteFormalType, ExistentialRepresentation::Opaque, *this)); ManagedValue mv = F(SGFContext(init.get())); if (!mv.isInContext()) { mv.forwardInto(*this, loc, init->getAddress()); init->finishInitialization(*this); } return manageBufferForExprResult(existential, existentialTL, C); } } }
/// Try to classify a conversion from non-existential type /// into an existential type by performing a static check /// of protocol conformances if it is possible. static DynamicCastFeasibility classifyDynamicCastToProtocol(ModuleDecl *M, CanType source, CanType target, bool isWholeModuleOpts) { assert(target.isExistentialType() && "target should be an existential type"); if (source == target) return DynamicCastFeasibility::WillSucceed; auto *TargetProtocol = cast_or_null<ProtocolDecl>(target.getAnyNominal()); if (!TargetProtocol) return DynamicCastFeasibility::MaySucceed; auto conformance = M->lookupConformance(source, TargetProtocol); if (conformance) { // A conditional conformance can have things that need to be evaluated // dynamically. if (conformance->getConditionalRequirements().empty()) return DynamicCastFeasibility::WillSucceed; return DynamicCastFeasibility::MaySucceed; } auto *SourceNominalTy = source.getAnyNominal(); if (!SourceNominalTy) return DynamicCastFeasibility::MaySucceed; // If we are casting a protocol, then the cast will fail // as we have not found any conformances and protocols cannot // be extended currently. // NOTE: If we allow protocol extensions in the future, this // conditional statement should be removed. if (isa<ProtocolType>(source)) { return DynamicCastFeasibility::WillFail; } // If it is a class and it can be proven that this class and its // superclasses cannot be extended, then it is safe to proceed. // No need to check this for structs, as they do not have any // superclasses. if (auto *CD = source.getClassOrBoundGenericClass()) { if (canClassOrSuperclassesHaveExtensions(CD, isWholeModuleOpts)) return DynamicCastFeasibility::MaySucceed; // Derived types may conform to the protocol. if (!CD->isFinal()) { // TODO: If it is a private type or internal type and we // can prove that there are no derived types conforming to a // protocol, then we can still return WillFail. return DynamicCastFeasibility::MaySucceed; } } // If the source type is file-private or target protocol is file-private, // then conformances cannot be changed at run-time, because only this // file could have implemented them, but no conformances were found. // Therefore it is safe to make a negative decision at compile-time. if (SourceNominalTy->getEffectiveAccess() <= AccessLevel::FilePrivate || TargetProtocol->getEffectiveAccess() <= AccessLevel::FilePrivate) { // This cast is always false. Replace it with a branch to the // failure block. return DynamicCastFeasibility::WillFail; } // AnyHashable is a special case: although it's a struct, there maybe another // type conforming to it and to the TargetProtocol at the same time. if (SourceNominalTy == SourceNominalTy->getASTContext().getAnyHashableDecl()) return DynamicCastFeasibility::MaySucceed; // If we are in a whole-module compilation and // if the source type is internal or target protocol is internal, // then conformances cannot be changed at run-time, because only this // module could have implemented them, but no conformances were found. // Therefore it is safe to make a negative decision at compile-time. if (isWholeModuleOpts && (SourceNominalTy->getEffectiveAccess() <= AccessLevel::Internal || TargetProtocol->getEffectiveAccess() <= AccessLevel::Internal)) { return DynamicCastFeasibility::WillFail; } return DynamicCastFeasibility::MaySucceed; }
/// Try to classify the dynamic-cast relationship between two types. DynamicCastFeasibility swift::classifyDynamicCast(ModuleDecl *M, CanType source, CanType target, bool isSourceTypeExact, bool isWholeModuleOpts) { if (source == target) return DynamicCastFeasibility::WillSucceed; auto sourceObject = source.getOptionalObjectType(); auto targetObject = target.getOptionalObjectType(); // A common level of optionality doesn't affect the feasibility, // except that we can't fold things to failure because nil inhabits // both types. if (sourceObject && targetObject) { return atWorst(classifyDynamicCast(M, sourceObject, targetObject), DynamicCastFeasibility::MaySucceed); // Casting to a more optional type follows the same rule unless we // know that the source cannot dynamically be an optional value, // in which case we'll always just cast and inject into an optional. } else if (targetObject) { auto result = classifyDynamicCast(M, source, targetObject, /* isSourceTypeExact */ false, isWholeModuleOpts); if (canDynamicallyStoreOptional(source)) result = atWorst(result, DynamicCastFeasibility::MaySucceed); return result; // Casting to a less-optional type can always fail. } else if (sourceObject) { return atBest(classifyDynamicCast(M, sourceObject, target, /* isSourceTypeExact */ false, isWholeModuleOpts), DynamicCastFeasibility::MaySucceed); } assert(!sourceObject && !targetObject); // Assume that casts to or from existential types or involving // dependent types can always succeed. This is over-conservative. if (source->hasArchetype() || source.isExistentialType() || target->hasArchetype() || target.isExistentialType()) { // Check conversions from non-protocol types into protocol types. if (!source.isExistentialType() && target.isExistentialType()) return classifyDynamicCastToProtocol(M, source, target, isWholeModuleOpts); // Check conversions from protocol types to non-protocol types. if (source.isExistentialType() && !target.isExistentialType()) return classifyDynamicCastFromProtocol(M, source, target, isWholeModuleOpts); return DynamicCastFeasibility::MaySucceed; } // Casts from AnyHashable. if (auto sourceStruct = dyn_cast<StructType>(source)) { if (sourceStruct->getDecl() == M->getASTContext().getAnyHashableDecl()) { if (auto hashable = getHashableExistentialType(M)) { // Succeeds if Hashable can be cast to the target type. return classifyDynamicCastFromProtocol(M, hashable, target, isWholeModuleOpts); } } } // Casts to AnyHashable. if (auto targetStruct = dyn_cast<StructType>(target)) { if (targetStruct->getDecl() == M->getASTContext().getAnyHashableDecl()) { // Succeeds if the source type can be dynamically cast to Hashable. // Hashable is not actually a legal existential type right now, but // the check doesn't care about that. if (auto hashable = getHashableExistentialType(M)) { return classifyDynamicCastToProtocol(M, source, hashable, isWholeModuleOpts); } } } // Metatype casts. if (auto sourceMetatype = dyn_cast<AnyMetatypeType>(source)) { auto targetMetatype = dyn_cast<AnyMetatypeType>(target); if (!targetMetatype) return DynamicCastFeasibility::WillFail; source = sourceMetatype.getInstanceType(); target = targetMetatype.getInstanceType(); if (source == target && targetMetatype.isAnyExistentialType() == sourceMetatype.isAnyExistentialType()) return DynamicCastFeasibility::WillSucceed; // If the source and target are the same existential type, but the source is // P.Protocol and the dest is P.Type, then we need to consider whether the // protocol is self-conforming. // The only cases where a protocol self-conforms are objc protocols, but // we're going to expect P.Type to hold a class object. And this case // doesn't matter since for a self-conforming protocol type there can't be // any type-level methods. // Thus we consider this kind of cast to always fail. The only exception // from this rule is when the target is Any.Type, because *.Protocol // can always be casted to Any.Type. if (source->isAnyExistentialType() && isa<MetatypeType>(sourceMetatype) && isa<ExistentialMetatypeType>(targetMetatype)) { return target->isAny() ? DynamicCastFeasibility::WillSucceed : DynamicCastFeasibility::WillFail; } if (targetMetatype.isAnyExistentialType() && (isa<ProtocolType>(target) || isa<ProtocolCompositionType>(target))) { auto Feasibility = classifyDynamicCastToProtocol(M, source, target, isWholeModuleOpts); // Cast from existential metatype to existential metatype may still // succeed, even if we cannot prove anything statically. if (Feasibility != DynamicCastFeasibility::WillFail || !sourceMetatype.isAnyExistentialType()) return Feasibility; } // If isSourceTypeExact is true, we know we are casting the result of a // MetatypeInst instruction. if (isSourceTypeExact) { // If source or target are existentials, then it can be cast // successfully only into itself. if ((target.isAnyExistentialType() || source.isAnyExistentialType()) && target != source) return DynamicCastFeasibility::WillFail; } // Casts from class existential metatype into a concrete non-class metatype // can never succeed. if (source->isClassExistentialType() && !target.isAnyExistentialType() && !target.getClassOrBoundGenericClass()) return DynamicCastFeasibility::WillFail; // TODO: prove that some conversions to existential metatype will // obviously succeed/fail. // TODO: prove that some conversions from class existential metatype // to a concrete non-class metatype will obviously fail. // TODO: class metatype to/from AnyObject // TODO: protocol concrete metatype to/from ObjCProtocol if (isa<ExistentialMetatypeType>(sourceMetatype) || isa<ExistentialMetatypeType>(targetMetatype)) return (getAnyMetatypeDepth(source) == getAnyMetatypeDepth(target) ? DynamicCastFeasibility::MaySucceed : DynamicCastFeasibility::WillFail); // If both metatypes are class metatypes, check if classes can be // cast. if (source.getClassOrBoundGenericClass() && target.getClassOrBoundGenericClass()) return classifyClassHierarchyCast(source, target); // Different structs cannot be cast to each other. if (source.getStructOrBoundGenericStruct() && target.getStructOrBoundGenericStruct() && source != target) return DynamicCastFeasibility::WillFail; // Different enums cannot be cast to each other. if (source.getEnumOrBoundGenericEnum() && target.getEnumOrBoundGenericEnum() && source != target) return DynamicCastFeasibility::WillFail; // If we don't know any better, assume that the cast may succeed. return DynamicCastFeasibility::MaySucceed; } // Function casts. if (auto sourceFunction = dyn_cast<FunctionType>(source)) { if (auto targetFunction = dyn_cast<FunctionType>(target)) { // A function cast can succeed if the function types can be identical, // or if the target type is throwier than the original. // A non-throwing source function can be cast to a throwing target type, // but not vice versa. if (sourceFunction->throws() && !targetFunction->throws()) return DynamicCastFeasibility::WillFail; // The cast can't change the representation at runtime. if (targetFunction->getRepresentation() != sourceFunction->getRepresentation()) return DynamicCastFeasibility::WillFail; if (sourceFunction.getInput() == targetFunction.getInput() && sourceFunction.getResult() == targetFunction.getResult()) return DynamicCastFeasibility::WillSucceed; auto isSubstitutable = [](CanType a, CanType b) -> bool { // FIXME: Unnecessarily conservative; should structurally check for // substitutability. return a == b || a->hasArchetype() || b->hasArchetype(); }; if (isSubstitutable(sourceFunction.getInput(), targetFunction.getInput()) && isSubstitutable(targetFunction.getInput(), targetFunction.getResult())) return DynamicCastFeasibility::MaySucceed; return DynamicCastFeasibility::WillFail; } } // Tuple casts. if (auto sourceTuple = dyn_cast<TupleType>(source)) { if (auto targetTuple = dyn_cast<TupleType>(target)) { // # of elements must coincide. if (sourceTuple->getNumElements() != targetTuple->getNumElements()) return DynamicCastFeasibility::WillFail; DynamicCastFeasibility result = DynamicCastFeasibility::WillSucceed; for (unsigned i : range(sourceTuple->getNumElements())) { const auto &sourceElt = sourceTuple->getElement(i); const auto &targetElt = targetTuple->getElement(i); // If both have names and the names mismatch, the cast will fail. if (sourceElt.hasName() && targetElt.hasName() && sourceElt.getName() != targetElt.getName()) return DynamicCastFeasibility::WillFail; // Combine the result of prior elements with this element type. result = std::max(result, classifyDynamicCast(M, sourceElt.getType()->getCanonicalType(), targetElt.getType()->getCanonicalType(), isSourceTypeExact, isWholeModuleOpts)); // If this element failed, we're done. if (result == DynamicCastFeasibility::WillFail) break; } return result; } } // Class casts. auto sourceClass = source.getClassOrBoundGenericClass(); auto targetClass = target.getClassOrBoundGenericClass(); if (sourceClass) { if (targetClass) { // Imported Objective-C generics don't check the generic parameters, which // are lost at runtime. if (sourceClass->usesObjCGenericsModel()) { if (sourceClass == targetClass) return DynamicCastFeasibility::WillSucceed; if (targetClass->usesObjCGenericsModel()) { // If both classes are ObjC generics, the cast may succeed if the // classes are related, irrespective of their generic parameters. auto isDeclSuperclass = [&](ClassDecl *proposedSuper, ClassDecl *proposedSub) -> bool { do { if (proposedSuper == proposedSub) return true; } while ((proposedSub = proposedSub->getSuperclassDecl())); return false; }; if (isDeclSuperclass(sourceClass, targetClass)) return DynamicCastFeasibility::MaySucceed; if (isDeclSuperclass(targetClass, sourceClass)) { return DynamicCastFeasibility::WillSucceed; } return DynamicCastFeasibility::WillFail; } } // Try a hierarchy cast. If that isn't failure, we can report it. auto hierarchyResult = classifyClassHierarchyCast(source, target); if (hierarchyResult != DynamicCastFeasibility::WillFail) return hierarchyResult; // As a backup, consider whether either type is a CF class type // with an NS bridged equivalent. CanType bridgedSource = getNSBridgedClassOfCFClass(M, source); CanType bridgedTarget = getNSBridgedClassOfCFClass(M, target); // If neither type qualifies, we're done. if (!bridgedSource && !bridgedTarget) return DynamicCastFeasibility::WillFail; // Otherwise, map over to the bridged types and try to answer the // question there. if (bridgedSource) source = bridgedSource; if (bridgedTarget) target = bridgedTarget; return classifyDynamicCast(M, source, target, false, isWholeModuleOpts); } // Casts from a class into a non-class can never succeed if the target must // be bridged to a SwiftValueBox. You would need an AnyObject source for // that. if (!target.isAnyExistentialType() && !target.getClassOrBoundGenericClass() && !isa<ArchetypeType>(target) && mustBridgeToSwiftValueBox(M, target)) { assert((target.getEnumOrBoundGenericEnum() || target.getStructOrBoundGenericStruct() || isa<TupleType>(target) || isa<SILFunctionType>(target) || isa<FunctionType>(target) || isa<MetatypeType>(target)) && "Target should be an enum, struct, tuple, metatype or function type"); return DynamicCastFeasibility::WillFail; } // In the Objective-C runtime, class metatypes are also class instances. // The cast may succeed if the target type can be inhabited by a class // metatype. // TODO: Narrow this to the sourceClass being exactly NSObject. if (M->getASTContext().LangOpts.EnableObjCInterop) { if (auto targetMeta = dyn_cast<MetatypeType>(target)) { if (isa<ArchetypeType>(targetMeta.getInstanceType()) || targetMeta.getInstanceType()->mayHaveSuperclass()) return DynamicCastFeasibility::MaySucceed; } else if (isa<ExistentialMetatypeType>(target)) { return DynamicCastFeasibility::MaySucceed; } } } // If the source is not existential, an archetype, or (under the ObjC runtime) // a class, and the destination is a metatype, there is no way the cast can // succeed. if (target->is<AnyMetatypeType>()) return DynamicCastFeasibility::WillFail; // FIXME: Be more careful with bridging conversions from // NSArray, NSDictionary and NSSet as they may fail? // We know that a cast from Int -> class foobar will fail. if (targetClass && !source.isAnyExistentialType() && !source.getClassOrBoundGenericClass() && !isa<ArchetypeType>(source) && mustBridgeToSwiftValueBox(M, source)) { assert((source.getEnumOrBoundGenericEnum() || source.getStructOrBoundGenericStruct() || isa<TupleType>(source) || isa<SILFunctionType>(source) || isa<FunctionType>(source) || isa<MetatypeType>(source)) && "Source should be an enum, struct, tuple, metatype or function type"); return DynamicCastFeasibility::WillFail; } // Check if there might be a bridging conversion. if (source->isBridgeableObjectType() && mayBridgeToObjectiveC(M, target)) { // Try to get the ObjC type which is bridged to target type. assert(!target.isAnyExistentialType()); // ObjC-to-Swift casts may fail. And in most cases it is impossible to // statically predict the outcome. So, let's be conservative here. return DynamicCastFeasibility::MaySucceed; } if (target->isBridgeableObjectType() && mayBridgeToObjectiveC(M, source)) { // Try to get the ObjC type which is bridged to source type. assert(!source.isAnyExistentialType()); if (Type ObjCTy = M->getASTContext().getBridgedToObjC(M, source)) { // If the bridged ObjC type is known, check if // this type can be cast into target type. return classifyDynamicCast(M, ObjCTy->getCanonicalType(), target, /* isSourceTypeExact */ false, isWholeModuleOpts); } return DynamicCastFeasibility::MaySucceed; } // Check if it is a cast between bridged error types. if (isError(M, source) && isError(M, target)) { // TODO: Cast to NSError succeeds always. return DynamicCastFeasibility::MaySucceed; } // Check for a viable collection cast. if (auto sourceStruct = dyn_cast<BoundGenericStructType>(source)) { if (auto targetStruct = dyn_cast<BoundGenericStructType>(target)) { // Both types have to be the same kind of collection. auto typeDecl = sourceStruct->getDecl(); if (typeDecl == targetStruct->getDecl()) { auto sourceArgs = sourceStruct.getGenericArgs(); auto targetArgs = targetStruct.getGenericArgs(); // Note that we can never say that a collection cast is impossible: // a cast can always succeed on an empty collection. // Arrays and sets. if (typeDecl == M->getASTContext().getArrayDecl() || typeDecl == M->getASTContext().getSetDecl()) { auto valueFeasibility = classifyDynamicCast(M, sourceArgs[0], targetArgs[0]); return atWorst(valueFeasibility, DynamicCastFeasibility::MaySucceed); // Dictionaries. } else if (typeDecl == M->getASTContext().getDictionaryDecl()) { auto keyFeasibility = classifyDynamicCast(M, sourceArgs[0], targetArgs[0]); auto valueFeasibility = classifyDynamicCast(M, sourceArgs[1], targetArgs[1]); return atWorst(atBest(keyFeasibility, valueFeasibility), DynamicCastFeasibility::MaySucceed); } } } } return DynamicCastFeasibility::WillFail; }
/// Given that a type is not statically known to be an optional type, check /// whether it might dynamically be able to store an optional. static bool canDynamicallyStoreOptional(CanType type) { assert(!type.getOptionalObjectType()); return type->canDynamicallyBeOptionalType(/* includeExistential */ true); }
/// Can the given cast be performed by the scalar checked-cast /// instructions? bool swift::canUseScalarCheckedCastInstructions(SILModule &M, CanType sourceType, CanType targetType) { // Look through one level of optionality on the source. auto objectType = sourceType; if (auto type = objectType.getOptionalObjectType()) objectType = type; // Casting to NSError needs to go through the indirect-cast case, // since it may conform to Error and require Error-to-NSError // bridging, unless we can statically see that the source type inherits // NSError. // A class-constrained archetype may be bound to NSError, unless it has a // non-NSError superclass constraint. Casts to archetypes thus must always be // indirect. if (auto archetype = targetType->getAs<ArchetypeType>()) { // Only ever permit this if the source type is a reference type. if (!objectType.isAnyClassReferenceType()) return false; if (M.getASTContext().LangOpts.EnableObjCInterop) { auto super = archetype->getSuperclass(); if (super.isNull()) return false; // A base class constraint that isn't NSError rules out the archetype being // bound to NSError. if (auto nserror = M.Types.getNSErrorType()) return !super->isEqual(nserror); // If NSError wasn't loaded, any base class constraint must not be NSError. return true; } else { // If ObjC bridging isn't enabled, we can do a scalar cast from any // reference type to any class-constrained archetype. return archetype->requiresClass(); } } if (M.getASTContext().LangOpts.EnableObjCInterop && targetType == M.Types.getNSErrorType()) { // If we statically know the source is an NSError subclass, then the cast // can go through the scalar path (and it's trivially true so can be // killed). return targetType->isExactSuperclassOf(objectType); } // Three supported cases: // - metatype to metatype // - metatype to object // - object to object if ((objectType.isAnyClassReferenceType() || isa<AnyMetatypeType>(objectType)) && targetType.isAnyClassReferenceType()) return true; if (isa<AnyMetatypeType>(objectType) && isa<AnyMetatypeType>(targetType)) return true; // Otherwise, we need to use the general indirect-cast functions. return false; }
/// Propagate information about a concrete type from init_existential_addr /// or init_existential_ref into witness_method conformances and into /// apply instructions. /// This helps the devirtualizer to replace witness_method by /// class_method instructions and then devirtualize. SILInstruction * SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI, ProtocolDecl *Protocol, llvm::function_ref<void(CanType , ProtocolConformanceRef)> Propagate) { // Get the self argument. SILValue Self; if (auto *Apply = dyn_cast<ApplyInst>(AI)) { if (Apply->hasSelfArgument()) Self = Apply->getSelfArgument(); } else if (auto *Apply = dyn_cast<TryApplyInst>(AI)) { if (Apply->hasSelfArgument()) Self = Apply->getSelfArgument(); } assert(Self && "Self argument should be present"); // Try to find the init_existential, which could be used to // determine a concrete type of the self. CanType OpenedArchetype; SILValue OpenedArchetypeDef; SILInstruction *InitExistential = findInitExistential(AI, Self, OpenedArchetype, OpenedArchetypeDef); if (!InitExistential) return nullptr; // Try to derive the concrete type of self and a related conformance from // the found init_existential. ArrayRef<ProtocolConformanceRef> Conformances; auto NewSelf = SILValue(); auto ConformanceAndConcreteType = getConformanceAndConcreteType(AI, InitExistential, Protocol, NewSelf, Conformances); if (!ConformanceAndConcreteType) return nullptr; ProtocolConformanceRef Conformance = std::get<0>(*ConformanceAndConcreteType); CanType ConcreteType = std::get<1>(*ConformanceAndConcreteType); SILValue ConcreteTypeDef = std::get<2>(*ConformanceAndConcreteType); SILOpenedArchetypesTracker *OldOpenedArchetypesTracker = Builder.getOpenedArchetypesTracker(); SILOpenedArchetypesTracker OpenedArchetypesTracker(*AI.getFunction()); if (ConcreteType->isOpenedExistential()) { // Prepare a mini-mapping for opened archetypes. // SILOpenedArchetypesTracker OpenedArchetypesTracker(*AI.getFunction()); OpenedArchetypesTracker.addOpenedArchetypeDef(ConcreteType, ConcreteTypeDef); Builder.setOpenedArchetypesTracker(&OpenedArchetypesTracker); } // Propagate the concrete type into the callee-operand if required. Propagate(ConcreteType, Conformance); // Create a new apply instruction that uses the concrete type instead // of the existential type. auto *NewAI = createApplyWithConcreteType(AI, NewSelf, Self, ConcreteType, ConcreteTypeDef, Conformance, OpenedArchetype); if (ConcreteType->isOpenedExistential()) Builder.setOpenedArchetypesTracker(OldOpenedArchetypesTracker); return NewAI; }
clang::CanQualType ClangTypeConverter::reverseBuiltinTypeMapping(IRGenModule &IGM, CanStructType type) { // Handle builtin types by adding entries to the cache that reverse // the mapping done by the importer. We could try to look at the // members of the struct instead, but even if that's ABI-equivalent // (which it had better be!), it might erase interesting semantic // differences like integers vs. characters. This is important // because CC lowering isn't the only purpose of this conversion. // // The importer maps builtin types like 'int' to named types like // 'CInt', which are generally typealiases. So what we do here is // map the underlying types of those typealiases back to the builtin // type. These typealiases frequently create a many-to-one mapping, // so just use the first type that mapped to a particular underlying // type. // // This is the last thing that happens before asserting that the // struct type doesn't have a mapping. Furthermore, all of the // builtin types are pre-built in the clang ASTContext. So it's not // really a significant performance problem to just cache all them // right here; it makes making a few more entries in the cache than // we really need, but it also means we won't end up repeating these // stdlib lookups multiple times, and we have to perform multiple // lookups anyway because the MAP_BUILTIN_TYPE database uses // typealias names (like 'CInt') that aren't obviously associated // with the underlying C library type. auto stdlib = IGM.Context.getStdlibModule(); assert(stdlib && "translating stdlib type to C without stdlib module?"); auto &ctx = IGM.getClangASTContext(); auto cacheStdlibType = [&](StringRef swiftName, clang::BuiltinType::Kind builtinKind) { CanType swiftType = getNamedSwiftType(stdlib, swiftName); if (!swiftType) return; auto &sema = IGM.Context.getClangModuleLoader()->getClangSema(); // Handle Int and UInt specially. On Apple platforms, these correspond to // the NSInteger and NSUInteger typedefs, so map them back to those typedefs // if they're available, to ensure we get consistent ObjC @encode strings. if (swiftType->getAnyNominal() == IGM.Context.getIntDecl()) { if (auto NSIntegerTy = getClangBuiltinTypeFromTypedef(sema, "NSInteger")) { Cache.insert({swiftType, NSIntegerTy}); return; } } else if (swiftType->getAnyNominal() == IGM.Context.getUIntDecl()) { if (auto NSUIntegerTy = getClangBuiltinTypeFromTypedef(sema, "NSUInteger")) { Cache.insert({swiftType, NSUIntegerTy}); return; } } Cache.insert({swiftType, getClangBuiltinTypeFromKind(ctx, builtinKind)}); }; #define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \ cacheStdlibType(#SWIFT_TYPE_NAME, clang::BuiltinType::CLANG_BUILTIN_KIND); #include "swift/ClangImporter/BuiltinMappedTypes.def" // The above code sets up a bunch of mappings in the cache; just // assume that we hit one of them. auto it = Cache.find(type); assert(it != Cache.end() && "cannot translate Swift type to C! type is not specially known"); return it->second; }
void SubstitutionMap:: addConformance(CanType type, ProtocolConformanceRef conformance) { conformanceMap[type.getPointer()].push_back(conformance); }