static bool areOnlyAbstractionDifferent(CanType type1, CanType type2) { assert(type1->isLegalSILType()); assert(type2->isLegalSILType()); // Exact equality is fine. if (type1 == type2) return true; // Either both types should be optional or neither should be. if (auto object1 = type1.getOptionalObjectType()) { auto object2 = type2.getOptionalObjectType(); if (!object2) return false; return areOnlyAbstractionDifferent(object1, object2); } if (type2.getOptionalObjectType()) return false; // Either both types should be tuples or neither should be. if (auto tuple1 = dyn_cast<TupleType>(type1)) { auto tuple2 = dyn_cast<TupleType>(type2); if (!tuple2) return false; if (tuple1->getNumElements() != tuple2->getNumElements()) return false; for (auto i : indices(tuple2->getElementTypes())) if (!areOnlyAbstractionDifferent(tuple1.getElementType(i), tuple2.getElementType(i))) return false; return true; } if (isa<TupleType>(type2)) return false; // Either both types should be metatypes or neither should be. if (auto meta1 = dyn_cast<AnyMetatypeType>(type1)) { auto meta2 = dyn_cast<AnyMetatypeType>(type2); if (!meta2) return false; if (meta1.getInstanceType() != meta2.getInstanceType()) return false; return true; } // Either both types should be functions or neither should be. if (auto fn1 = dyn_cast<SILFunctionType>(type1)) { auto fn2 = dyn_cast<SILFunctionType>(type2); if (!fn2) return false; // TODO: maybe there are checks we can do here? (void)fn1; (void)fn2; return true; } if (isa<SILFunctionType>(type2)) return false; llvm_unreachable("no other types should differ by abstraction"); }
bool AbstractionPattern::hasSameBasicTypeStructure(CanType l, CanType r) { if (l == r) return true; // Tuples must match. auto lTuple = dyn_cast<TupleType>(l); auto rTuple = dyn_cast<TupleType>(r); if (lTuple && rTuple) { auto lElts = lTuple.getElementTypes(); auto rElts = rTuple.getElementTypes(); if (lElts.size() != rElts.size()) return false; for (auto i : indices(lElts)) { if (!hasSameBasicTypeStructure(lElts[i], rElts[i])) return false; } return true; } else if (lTuple || rTuple) { return false; } // Functions must match. auto lFunction = dyn_cast<AnyFunctionType>(l); auto rFunction = dyn_cast<AnyFunctionType>(r); if (lFunction && rFunction) { auto lParam = lFunction.getParams(); auto rParam = rFunction.getParams(); if (lParam.size() != rParam.size()) return false; for (unsigned i : indices(lParam)) { if (!hasSameBasicTypeStructure(lParam[i].getPlainType(), rParam[i].getPlainType())) return false; } return hasSameBasicTypeStructure(lFunction.getResult(), rFunction.getResult()); } else if (lFunction || rFunction) { return false; } // Optionals must match, sortof. auto lObject = l.getOptionalObjectType(); auto rObject = r.getOptionalObjectType(); if (lObject && rObject) { return hasSameBasicTypeStructure(lObject, rObject); } else if (lObject || rObject) { // Allow optionality mis-matches, but require the underlying types to match. return hasSameBasicTypeStructure(lObject ? lObject : l, rObject ? rObject : r); } // Otherwise, the structure is similar enough. return true; }
static unsigned getOptionalDepth(CanType type) { unsigned depth = 0; while (CanType objectType = type.getOptionalObjectType()) { depth++; type = objectType; } return depth; }
bool SILType::isLoweringOf(SILModule &Mod, CanType formalType) { SILType loweredType = *this; // Optional lowers its contained type. The difference between Optional // and IUO is lowered away. SILType loweredObjectType = loweredType.getOptionalObjectType(); CanType formalObjectType = formalType.getOptionalObjectType(); if (loweredObjectType) { return formalObjectType && loweredObjectType.isLoweringOf(Mod, formalObjectType); } // Metatypes preserve their instance type through lowering. if (loweredType.is<MetatypeType>()) { if (auto formalMT = dyn_cast<MetatypeType>(formalType)) { return loweredType.getMetatypeInstanceType(Mod).isLoweringOf( Mod, formalMT.getInstanceType()); } } if (auto loweredEMT = loweredType.getAs<ExistentialMetatypeType>()) { if (auto formalEMT = dyn_cast<ExistentialMetatypeType>(formalType)) { return loweredEMT.getInstanceType() == formalEMT.getInstanceType(); } } // TODO: Function types go through a more elaborate lowering. // For now, just check that a SIL function type came from some AST function // type. if (loweredType.is<SILFunctionType>()) return isa<AnyFunctionType>(formalType); // Tuples are lowered elementwise. // TODO: Will this always be the case? if (auto loweredTT = loweredType.getAs<TupleType>()) { if (auto formalTT = dyn_cast<TupleType>(formalType)) { if (loweredTT->getNumElements() != formalTT->getNumElements()) return false; for (unsigned i = 0, e = loweredTT->getNumElements(); i < e; ++i) { auto loweredTTEltType = SILType::getPrimitiveAddressType(loweredTT.getElementType(i)); if (!loweredTTEltType.isLoweringOf(Mod, formalTT.getElementType(i))) return false; } return true; } } // Dynamic self has the same lowering as its contained type. if (auto dynamicSelf = dyn_cast<DynamicSelfType>(formalType)) formalType = dynamicSelf.getSelfType(); // Other types are preserved through lowering. return loweredType.getASTType() == formalType; }
/// 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); }
static CanType getOptionalObjectType(CanType type) { auto objectType = type.getOptionalObjectType(); assert(objectType && "type was not optional"); return objectType; }