bool FulfillmentMap::searchNominalTypeMetadata(IRGenModule &IGM, CanType type, MetadataState metadataState, unsigned source, MetadataPath &&path, const InterestingKeysCallback &keys) { // Objective-C generics don't preserve their generic parameters at runtime, // so they aren't able to fulfill type metadata requirements. if (type.getAnyNominal()->hasClangNode()) { return false; } auto *nominal = type.getAnyNominal(); if (!nominal->isGenericContext() || isa<ProtocolDecl>(nominal)) { return false; } bool hadFulfillment = false; GenericTypeRequirements requirements(IGM, nominal); requirements.enumerateFulfillments( IGM, type->getContextSubstitutionMap(IGM.getSwiftModule(), nominal), [&](unsigned reqtIndex, CanType arg, Optional<ProtocolConformanceRef> conf) { // Skip uninteresting type arguments. if (!keys.hasInterestingType(arg)) return; // If the fulfilled value is type metadata, refine the path. if (!conf) { auto argState = getPresumedMetadataStateForTypeArgument(metadataState); MetadataPath argPath = path; argPath.addNominalTypeArgumentComponent(reqtIndex); hadFulfillment |= searchTypeMetadata(IGM, arg, IsExact, argState, source, std::move(argPath), keys); return; } // Otherwise, it's a conformance. // Ignore it unless the type itself is interesting. if (!keys.isInterestingType(arg)) return; // Refine the path. MetadataPath argPath = path; argPath.addNominalTypeArgumentConformanceComponent(reqtIndex); hadFulfillment |= searchWitnessTable(IGM, arg, conf->getRequirement(), source, std::move(argPath), keys); }); return hadFulfillment; }
static bool mustBridgeToSwiftValueBox(Module *M, CanType T) { // If the target type is either an unknown dynamic type, or statically // known to bridge, the cast may succeed. if (T->hasArchetype()) return false; if (T->isAnyExistentialType()) return false; // getBridgedToObjC() might return a null-type for bridged foundation types // during compiling the standard library. Exclude this case here. if (auto N = T->getAnyNominal()) if (M->getASTContext().isStandardLibraryTypeBridgedInFoundation(N)) return false; auto bridgeTy = M->getASTContext().getBridgedToObjC(M, T, nullptr); if (!bridgeTy.hasValue()) return false; if (bridgeTy->isNull()) return true; return false; }
void irgen::applyLayoutAttributes(IRGenModule &IGM, CanType ASTTy, bool IsFixedLayout, Alignment &MinimumAlign) { assert(ASTTy && "shouldn't call applyLayoutAttributes without a type"); auto &Diags = IGM.Context.Diags; auto decl = ASTTy->getAnyNominal(); if (!decl) return; if (auto alignment = decl->getAttrs().getAttribute<AlignmentAttr>()) { auto value = alignment->getValue(); assert(value != 0 && ((value - 1) & value) == 0 && "alignment not a power of two!"); if (!IsFixedLayout) Diags.diagnose(alignment->getLocation(), diag::alignment_dynamic_type_layout_unsupported); else if (value < MinimumAlign.getValue()) Diags.diagnose(alignment->getLocation(), diag::alignment_less_than_natural, MinimumAlign.getValue()); else MinimumAlign = Alignment(value); } }
static bool mustBridgeToSwiftValueBox(ModuleDecl *M, CanType T) { // If the target type is either an unknown dynamic type, or statically // known to bridge, the cast may succeed. if (T->hasArchetype()) return false; if (T->isAnyExistentialType()) return false; // getBridgedToObjC() might return a null-type for some types // whose bridging implementation is allowed to live elsewhere. Exclude this // case here. if (auto N = T->getAnyNominal()) if (M->getASTContext().isTypeBridgedInExternalModule(N)) return false; return !M->getASTContext().getBridgedToObjC(M, T); }
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); assert(!isa<PolymorphicFunctionType>(type) && "Don't expect a polymorphic function type here"); return false; // continue searching }); return result; }
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; }
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; }
/// 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(Module *M, CanType source, CanType target, bool isSourceTypeExact, bool isWholeModuleOpts) { if (source == target) return DynamicCastFeasibility::WillSucceed; auto sourceObject = source.getAnyOptionalObjectType(); auto targetObject = target.getAnyOptionalObjectType(); // 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 (canDynamicallyBeOptionalType(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()) { auto *SourceNominalTy = source.getAnyNominal(); // Check conversions from non-protocol types into protocol types. if (!source.isExistentialType() && SourceNominalTy && target.isExistentialType()) return classifyDynamicCastToProtocol(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(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 (targetMetatype.isAnyExistentialType() && (isa<ProtocolType>(target) || isa<ProtocolCompositionType>(target))) { auto Feasibility = classifyDynamicCastToProtocol(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; } } // 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: tuple conversions? // 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()); if (Type ObjCTy = M->getASTContext().getBridgedToObjC(M, target)) { // If the bridged ObjC type is known, check if // source type can be cast into it. return classifyDynamicCast(M, source, ObjCTy.getCanonicalTypeOrNull(), /* isSourceTypeExact */ false, isWholeModuleOpts); } 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.getCanonicalTypeOrNull(), 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; }
/// Perform structure layout on the given types. StructLayout::StructLayout(IRGenModule &IGM, CanType astTy, LayoutKind layoutKind, LayoutStrategy strategy, ArrayRef<const TypeInfo *> types, llvm::StructType *typeToFill) { ASTTy = astTy; Elements.reserve(types.size()); // Fill in the Elements array. for (auto type : types) Elements.push_back(ElementLayout::getIncomplete(*type, *type)); assert(typeToFill == nullptr || typeToFill->isOpaque()); StructLayoutBuilder builder(IGM); // Add the heap header if necessary. if (requiresHeapHeader(layoutKind)) { builder.addHeapHeader(); } bool nonEmpty = builder.addFields(Elements, strategy); // Special-case: there's nothing to store. // In this case, produce an opaque type; this tends to cause lovely // assertions. if (!nonEmpty) { assert(!builder.empty() == requiresHeapHeader(layoutKind)); MinimumAlign = Alignment(1); MinimumSize = Size(0); SpareBits.clear(); IsFixedLayout = true; IsKnownPOD = IsPOD; IsKnownBitwiseTakable = IsBitwiseTakable; IsKnownAlwaysFixedSize = IsFixedSize; Ty = (typeToFill ? typeToFill : IGM.OpaquePtrTy->getElementType()); } else { MinimumAlign = builder.getAlignment(); MinimumSize = builder.getSize(); SpareBits = std::move(builder.getSpareBits()); IsFixedLayout = builder.isFixedLayout(); IsKnownPOD = builder.isPOD(); IsKnownBitwiseTakable = builder.isBitwiseTakable(); IsKnownAlwaysFixedSize = builder.isAlwaysFixedSize(); if (typeToFill) { builder.setAsBodyOfStruct(typeToFill); Ty = typeToFill; } else { Ty = builder.getAsAnonStruct(); } } // If the struct is not @_fixed_layout, it will have a dynamic // layout outside of its resilience domain. if (astTy && astTy->getAnyNominal()) if (IGM.isResilient(astTy->getAnyNominal(), ResilienceExpansion::Minimal)) IsKnownAlwaysFixedSize = IsNotFixedSize; assert(typeToFill == nullptr || Ty == typeToFill); if (ASTTy) applyLayoutAttributes(IGM, ASTTy, IsFixedLayout, MinimumAlign); }
/// 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(CanType source, CanType target, bool isWholeModuleOpts) { assert(target.isExistentialType() && "target should be an existential type"); if (source == target) return DynamicCastFeasibility::WillSucceed; auto *SourceNominalTy = source.getAnyNominal(); if (!SourceNominalTy) return DynamicCastFeasibility::MaySucceed; auto *TargetProtocol = target.getAnyNominal(); if (!TargetProtocol) return DynamicCastFeasibility::MaySucceed; auto SourceProtocols = SourceNominalTy->getAllProtocols(); // Check all protocols implemented by the type. for (auto *Protocol : SourceProtocols) { if (Protocol == TargetProtocol) return DynamicCastFeasibility::WillSucceed; } // 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 private or target protocol is 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() == Accessibility::Private || TargetProtocol->getEffectiveAccess() == Accessibility::Private) { // This cast is always false. Replace it with a branch to the // failure block. return DynamicCastFeasibility::WillFail; } // 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() == Accessibility::Internal || TargetProtocol->getEffectiveAccess() == Accessibility::Internal)) { return DynamicCastFeasibility::WillFail; } return DynamicCastFeasibility::MaySucceed; }
/// Try to classify the dynamic-cast relationship between two types. DynamicCastFeasibility swift::classifyDynamicCast(Module *M, CanType source, CanType target, bool isSourceTypeExact, bool isWholeModuleOpts) { if (source == target) return DynamicCastFeasibility::WillSucceed; auto sourceObject = source.getAnyOptionalObjectType(); auto targetObject = target.getAnyOptionalObjectType(); // A common level of optionality doesn't affect the feasibility. if (sourceObject && targetObject) { return classifyDynamicCast(M, sourceObject, targetObject); // Nor does casting to a more optional type. } else if (targetObject) { return classifyDynamicCast(M, source, targetObject, /* isSourceTypeExact */ false, isWholeModuleOpts); // Casting to a less-optional type can always fail. } else if (sourceObject) { return weakenSuccess(classifyDynamicCast(M, sourceObject, target, /* isSourceTypeExact */ false, isWholeModuleOpts)); } 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()) { auto *SourceNominalTy = source.getAnyNominal(); // Check conversions from non-protocol types into protocol types. if (!source.isExistentialType() && SourceNominalTy && target.isExistentialType()) return classifyDynamicCastToProtocol(source, target, isWholeModuleOpts); // 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; } return DynamicCastFeasibility::MaySucceed; } // 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 (targetMetatype.isAnyExistentialType() && (isa<ProtocolType>(target) || isa<ProtocolCompositionType>(target))) { auto Feasibility = classifyDynamicCastToProtocol(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 classifyDynamicCast(M, source, target, false, isWholeModuleOpts); // 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; // A noreturn source function can be cast to a returning target type, // but not vice versa. // (noreturn isn't really reified at runtime though.) if (targetFunction->isNoReturn() && !sourceFunction->isNoReturn()) 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; } } // 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; } } if (target->isExactSuperclassOf(source, nullptr)) return DynamicCastFeasibility::WillSucceed; if (target->isBindableToSuperclassOf(source, nullptr)) return DynamicCastFeasibility::MaySucceed; if (source->isBindableToSuperclassOf(target, nullptr)) return DynamicCastFeasibility::MaySucceed; // FIXME: bridged types, e.g. CF <-> NS (but not for metatypes). 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: tuple conversions? // FIXME: Be more careful with bridging conversions from // NSArray, NSDictionary and NSSet as they may fail? // 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()); Optional<Type> ObjCTy = M->getASTContext().getBridgedToObjC( M, target, nullptr); if (ObjCTy && ObjCTy.getValue()) { // If the bridged ObjC type is known, check if // source type can be cast into it. return classifyDynamicCast(M, source, ObjCTy.getValue().getCanonicalTypeOrNull(), /* isSourceTypeExact */ false, isWholeModuleOpts); } return DynamicCastFeasibility::MaySucceed; } if (target->isBridgeableObjectType() && mayBridgeToObjectiveC(M, source)) { // Try to get the ObjC type which is bridged to source type. assert(!source.isAnyExistentialType()); Optional<Type> ObjCTy = M->getASTContext().getBridgedToObjC( M, source, nullptr); if (ObjCTy && ObjCTy.getValue()) { // If the bridged ObjC type is known, check if // this type can be cast into target type. return classifyDynamicCast(M, ObjCTy.getValue().getCanonicalTypeOrNull(), 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; } return DynamicCastFeasibility::WillFail; }
/// 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; // If conformsToProtocol returns a valid conformance, then all requirements // were proven by the type checker. if (M->conformsToProtocol(source, TargetProtocol)) return DynamicCastFeasibility::WillSucceed; auto *SourceNominalTy = source.getAnyNominal(); if (!SourceNominalTy) return DynamicCastFeasibility::MaySucceed; // Protocol types may conform to their own protocols (or other protocols) // in the future. if (source->isAnyExistentialType()) { return DynamicCastFeasibility::MaySucceed; } // 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; } } // The WillFail conditions below assume any possible conformance on the // nominal source type has been ruled out. The prior conformsToProtocol query // identified any definite conformance. Now check if there is already a known // conditional conformance on the nominal type with requirements that were // not proven. // // TODO: The TypeChecker can easily prove that some requirements cannot be // met. Returning WillFail in those cases would be more optimal. To do that, // the conformsToProtocol interface needs to be reformulated as a query, and // the implementation, including checkGenericArguments, needs to be taught to // recognize that types with archetypes may potentially succeed. if (auto conformance = M->lookupConformance(source, TargetProtocol)) { assert(!conformance->getConditionalRequirements().empty()); 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; }