mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Don't classify bridging casts as WillSucceed if the object-to-value cast can fail.
When casting from an object type to a bridged Swift value type, classifyDynamicCast would use the cast classification for the target type's bridged object type, which would be trivially WillSucceed for thinks like NSNumber-to-Int or NSError-to-SomeError, even though the bridging itself could fail. Fixing this fixes SR-2920|rdar://problem/31404281.
This commit is contained in:
@@ -425,6 +425,10 @@ public:
|
|||||||
|
|
||||||
/// Retrieve the declaration of Foundation.NSError.
|
/// Retrieve the declaration of Foundation.NSError.
|
||||||
ClassDecl *getNSErrorDecl() const;
|
ClassDecl *getNSErrorDecl() const;
|
||||||
|
/// Retrieve the declaration of Foundation.NSNumber.
|
||||||
|
ClassDecl *getNSNumberDecl() const;
|
||||||
|
/// Retrieve the declaration of Foundation.NSValue.
|
||||||
|
ClassDecl *getNSValueDecl() const;
|
||||||
|
|
||||||
// Declare accessors for the known declarations.
|
// Declare accessors for the known declarations.
|
||||||
#define FUNC_DECL(Name, Id) \
|
#define FUNC_DECL(Name, Id) \
|
||||||
@@ -487,6 +491,11 @@ public:
|
|||||||
/// module's overlay, for layering or implementation detail reasons.
|
/// module's overlay, for layering or implementation detail reasons.
|
||||||
bool isTypeBridgedInExternalModule(NominalTypeDecl *nominal) const;
|
bool isTypeBridgedInExternalModule(NominalTypeDecl *nominal) const;
|
||||||
|
|
||||||
|
/// True if the given type is an Objective-C class that serves as the bridged
|
||||||
|
/// object type for many Swift value types, meaning that the conversion from
|
||||||
|
/// an object to a value is a conditional cast.
|
||||||
|
bool isObjCClassWithMultipleSwiftBridgedTypes(Type t);
|
||||||
|
|
||||||
/// Get the Objective-C type that a Swift type bridges to, if any.
|
/// Get the Objective-C type that a Swift type bridges to, if any.
|
||||||
///
|
///
|
||||||
/// \param dc The context in which bridging is occurring.
|
/// \param dc The context in which bridging is occurring.
|
||||||
|
|||||||
@@ -83,6 +83,11 @@ using AssociativityCacheType =
|
|||||||
llvm::DenseMap<std::pair<PrecedenceGroupDecl *, PrecedenceGroupDecl *>,
|
llvm::DenseMap<std::pair<PrecedenceGroupDecl *, PrecedenceGroupDecl *>,
|
||||||
Associativity>;
|
Associativity>;
|
||||||
|
|
||||||
|
#define FOR_KNOWN_FOUNDATION_TYPES(MACRO) \
|
||||||
|
MACRO(NSError) \
|
||||||
|
MACRO(NSNumber) \
|
||||||
|
MACRO(NSValue)
|
||||||
|
|
||||||
struct ASTContext::Implementation {
|
struct ASTContext::Implementation {
|
||||||
Implementation();
|
Implementation();
|
||||||
~Implementation();
|
~Implementation();
|
||||||
@@ -153,8 +158,11 @@ struct ASTContext::Implementation {
|
|||||||
/// The declaration of ObjectiveC.ObjCBool.
|
/// The declaration of ObjectiveC.ObjCBool.
|
||||||
StructDecl *ObjCBoolDecl = nullptr;
|
StructDecl *ObjCBoolDecl = nullptr;
|
||||||
|
|
||||||
/// The declaration of Foundation.NSError.
|
#define CACHE_FOUNDATION_DECL(NAME) \
|
||||||
ClassDecl *NSErrorDecl = nullptr;
|
/** The declaration of Foundation.NAME. */ \
|
||||||
|
ClassDecl *NAME##Decl = nullptr;
|
||||||
|
FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL)
|
||||||
|
#undef CACHE_FOUNDATION_DECL
|
||||||
|
|
||||||
// Declare cached declarations for each of the known declarations.
|
// Declare cached declarations for each of the known declarations.
|
||||||
#define FUNC_DECL(Name, Id) FuncDecl *Get##Name = nullptr;
|
#define FUNC_DECL(Name, Id) FuncDecl *Get##Name = nullptr;
|
||||||
@@ -731,26 +739,31 @@ StructDecl *ASTContext::getObjCBoolDecl() const {
|
|||||||
return Impl.ObjCBoolDecl;
|
return Impl.ObjCBoolDecl;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassDecl *ASTContext::getNSErrorDecl() const {
|
#define GET_FOUNDATION_DECL(NAME) \
|
||||||
if (!Impl.NSErrorDecl) {
|
ClassDecl *ASTContext::get##NAME##Decl() const { \
|
||||||
if (ModuleDecl *M = getLoadedModule(Id_Foundation)) {
|
if (!Impl.NAME##Decl) { \
|
||||||
// Note: use unqualified lookup so we find NSError regardless of
|
if (ModuleDecl *M = getLoadedModule(Id_Foundation)) { \
|
||||||
// whether it's defined in the Foundation module or the Clang
|
/* Note: use unqualified lookup so we find NSError regardless of */ \
|
||||||
// Foundation module it imports.
|
/* whether it's defined in the Foundation module or the Clang */ \
|
||||||
UnqualifiedLookup lookup(getIdentifier("NSError"), M, nullptr);
|
/* Foundation module it imports. */ \
|
||||||
if (auto type = lookup.getSingleTypeResult()) {
|
UnqualifiedLookup lookup(getIdentifier(#NAME), M, nullptr); \
|
||||||
if (auto classDecl = dyn_cast<ClassDecl>(type)) {
|
if (auto type = lookup.getSingleTypeResult()) { \
|
||||||
if (classDecl->getGenericParams() == nullptr) {
|
if (auto classDecl = dyn_cast<ClassDecl>(type)) { \
|
||||||
Impl.NSErrorDecl = classDecl;
|
if (classDecl->getGenericParams() == nullptr) { \
|
||||||
}
|
Impl.NAME##Decl = classDecl; \
|
||||||
}
|
} \
|
||||||
}
|
} \
|
||||||
}
|
} \
|
||||||
}
|
} \
|
||||||
|
} \
|
||||||
return Impl.NSErrorDecl;
|
\
|
||||||
|
return Impl.NAME##Decl; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FOR_KNOWN_FOUNDATION_TYPES(GET_FOUNDATION_DECL)
|
||||||
|
#undef GET_FOUNDATION_DECL
|
||||||
|
#undef FOR_KNOWN_FOUNDATION_TYPES
|
||||||
|
|
||||||
ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
|
ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
|
||||||
// Check whether we've already looked for and cached this protocol.
|
// Check whether we've already looked for and cached this protocol.
|
||||||
unsigned index = (unsigned)kind;
|
unsigned index = (unsigned)kind;
|
||||||
@@ -4044,6 +4057,21 @@ bool ASTContext::isTypeBridgedInExternalModule(
|
|||||||
nominal->getParentModule()->getName() == Id_CoreMedia);
|
nominal->getParentModule()->getName() == Id_CoreMedia);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ASTContext::isObjCClassWithMultipleSwiftBridgedTypes(Type t) {
|
||||||
|
auto clas = t->getClassOrBoundGenericClass();
|
||||||
|
if (!clas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (clas == getNSErrorDecl())
|
||||||
|
return true;
|
||||||
|
if (clas == getNSNumberDecl())
|
||||||
|
return true;
|
||||||
|
if (clas == getNSValueDecl())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type,
|
Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type,
|
||||||
Type *bridgedValueType) const {
|
Type *bridgedValueType) const {
|
||||||
if (type->isBridgeableObjectType()) {
|
if (type->isBridgeableObjectType()) {
|
||||||
|
|||||||
@@ -217,8 +217,8 @@ bool swift::isError(ModuleDecl *M, CanType Ty) {
|
|||||||
M->getASTContext().getProtocol(KnownProtocolKind::Error);
|
M->getASTContext().getProtocol(KnownProtocolKind::Error);
|
||||||
|
|
||||||
if (errorTypeProto) {
|
if (errorTypeProto) {
|
||||||
// Find the conformance of the value type to _BridgedToObjectiveC.
|
// Find the conformance of the value type to Error.
|
||||||
// Check whether the type conforms to _BridgedToObjectiveC.
|
// Check whether the type conforms to Error.
|
||||||
auto conformance = M->lookupConformance(Ty, errorTypeProto, nullptr);
|
auto conformance = M->lookupConformance(Ty, errorTypeProto, nullptr);
|
||||||
return conformance.hasValue();
|
return conformance.hasValue();
|
||||||
}
|
}
|
||||||
@@ -636,9 +636,15 @@ swift::classifyDynamicCast(ModuleDecl *M,
|
|||||||
if (Type ObjCTy = M->getASTContext().getBridgedToObjC(M, target)) {
|
if (Type ObjCTy = M->getASTContext().getBridgedToObjC(M, target)) {
|
||||||
// If the bridged ObjC type is known, check if
|
// If the bridged ObjC type is known, check if
|
||||||
// source type can be cast into it.
|
// source type can be cast into it.
|
||||||
return classifyDynamicCast(M, source,
|
auto canCastToBridgedType = classifyDynamicCast(M, source,
|
||||||
ObjCTy->getCanonicalType(),
|
ObjCTy->getCanonicalType(),
|
||||||
/* isSourceTypeExact */ false, isWholeModuleOpts);
|
/* isSourceTypeExact */ false, isWholeModuleOpts);
|
||||||
|
// Temper our enthusiasm if the bridge from the ObjC type to the target
|
||||||
|
// value type may fail.
|
||||||
|
if (canCastToBridgedType == DynamicCastFeasibility::WillSucceed
|
||||||
|
&& M->getASTContext().isObjCClassWithMultipleSwiftBridgedTypes(ObjCTy))
|
||||||
|
return DynamicCastFeasibility::MaySucceed;
|
||||||
|
return canCastToBridgedType;
|
||||||
}
|
}
|
||||||
return DynamicCastFeasibility::MaySucceed;
|
return DynamicCastFeasibility::MaySucceed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1802,10 +1802,6 @@ simplifyCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *Inst) {
|
|||||||
isSourceTypeExact,
|
isSourceTypeExact,
|
||||||
Mod.isWholeModule());
|
Mod.isWholeModule());
|
||||||
|
|
||||||
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Feasibility == DynamicCastFeasibility::WillFail) {
|
if (Feasibility == DynamicCastFeasibility::WillFail) {
|
||||||
if (shouldDestroyOnFailure(Inst->getConsumptionKind())) {
|
if (shouldDestroyOnFailure(Inst->getConsumptionKind())) {
|
||||||
auto &srcTL = Builder.getModule().getTypeLowering(Src->getType());
|
auto &srcTL = Builder.getModule().getTypeLowering(Src->getType());
|
||||||
@@ -1817,60 +1813,67 @@ simplifyCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *Inst) {
|
|||||||
return NewI;
|
return NewI;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cast will succeed
|
|
||||||
|
|
||||||
// Replace by unconditional_addr_cast, followed by a branch.
|
|
||||||
// The unconditional_addr_cast can be skipped, if the result of a cast
|
|
||||||
// is not used afterwards.
|
|
||||||
bool ResultNotUsed = isa<AllocStackInst>(Dest);
|
bool ResultNotUsed = isa<AllocStackInst>(Dest);
|
||||||
for (auto Use : Dest->getUses()) {
|
if (ResultNotUsed) {
|
||||||
auto *User = Use->getUser();
|
for (auto Use : Dest->getUses()) {
|
||||||
if (isa<DeallocStackInst>(User) || User == Inst)
|
auto *User = Use->getUser();
|
||||||
continue;
|
if (isa<DeallocStackInst>(User) || User == Inst)
|
||||||
ResultNotUsed = false;
|
continue;
|
||||||
break;
|
ResultNotUsed = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *BB = Inst->getParent();
|
auto *BB = Inst->getParent();
|
||||||
|
|
||||||
if (!ResultNotUsed) {
|
SILInstruction *BridgedI = nullptr;
|
||||||
SILInstruction *BridgedI = nullptr;
|
|
||||||
|
|
||||||
// To apply the bridged optimizations, we should
|
// To apply the bridged optimizations, we should
|
||||||
// ensure that types are not existential,
|
// ensure that types are not existential,
|
||||||
// and that not both types are classes.
|
// and that not both types are classes.
|
||||||
BridgedI = optimizeBridgedCasts(Inst, Inst->getConsumptionKind(),
|
BridgedI = optimizeBridgedCasts(Inst, Inst->getConsumptionKind(),
|
||||||
true, Src, Dest, SourceType,
|
true, Src, Dest, SourceType,
|
||||||
TargetType, SuccessBB, FailureBB);
|
TargetType, SuccessBB, FailureBB);
|
||||||
|
|
||||||
if (!BridgedI) {
|
if (!BridgedI) {
|
||||||
// Since it is an addr cast, only address types are handled here.
|
// If the cast may succeed or fail, and it can't be optimized into a
|
||||||
if (!Src->getType().isAddress() || !Dest->getType().isAddress()) {
|
// bridging operation, then let it be.
|
||||||
return nullptr;
|
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
|
||||||
} else if (!emitSuccessfulIndirectUnconditionalCast(
|
return nullptr;
|
||||||
Builder, Mod.getSwiftModule(), Loc,
|
}
|
||||||
Inst->getConsumptionKind(), Src, SourceType, Dest,
|
|
||||||
TargetType, Inst)) {
|
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
|
||||||
// No optimization was possible.
|
|
||||||
return nullptr;
|
// Replace by unconditional_addr_cast, followed by a branch.
|
||||||
}
|
// The unconditional_addr_cast can be skipped, if the result of a cast
|
||||||
|
// is not used afterwards.
|
||||||
|
if (ResultNotUsed) {
|
||||||
EraseInstAction(Inst);
|
EraseInstAction(Inst);
|
||||||
}
|
|
||||||
SILInstruction *NewI = &BB->back();
|
|
||||||
if (!isa<TermInst>(NewI)) {
|
|
||||||
Builder.setInsertionPoint(BB);
|
Builder.setInsertionPoint(BB);
|
||||||
NewI = Builder.createBranch(Loc, SuccessBB);
|
auto *NewI = Builder.createBranch(Loc, SuccessBB);
|
||||||
|
WillSucceedAction();
|
||||||
|
return NewI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since it is an addr cast, only address types are handled here.
|
||||||
|
if (!Src->getType().isAddress() || !Dest->getType().isAddress()) {
|
||||||
|
return nullptr;
|
||||||
|
} else if (!emitSuccessfulIndirectUnconditionalCast(
|
||||||
|
Builder, Mod.getSwiftModule(), Loc,
|
||||||
|
Inst->getConsumptionKind(), Src, SourceType, Dest,
|
||||||
|
TargetType, Inst)) {
|
||||||
|
// No optimization was possible.
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
WillSucceedAction();
|
|
||||||
return NewI;
|
|
||||||
} else {
|
|
||||||
// Result is not used.
|
|
||||||
EraseInstAction(Inst);
|
EraseInstAction(Inst);
|
||||||
Builder.setInsertionPoint(BB);
|
|
||||||
auto *NewI = Builder.createBranch(Loc, SuccessBB);
|
|
||||||
WillSucceedAction();
|
|
||||||
return NewI;
|
|
||||||
}
|
}
|
||||||
|
SILInstruction *NewI = &BB->back();
|
||||||
|
if (!isa<TermInst>(NewI)) {
|
||||||
|
Builder.setInsertionPoint(BB);
|
||||||
|
NewI = Builder.createBranch(Loc, SuccessBB);
|
||||||
|
}
|
||||||
|
WillSucceedAction();
|
||||||
|
return NewI;
|
||||||
}
|
}
|
||||||
|
|
||||||
SILInstruction *
|
SILInstruction *
|
||||||
@@ -1926,10 +1929,6 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) {
|
|||||||
TargetType,
|
TargetType,
|
||||||
isSourceTypeExact);
|
isSourceTypeExact);
|
||||||
|
|
||||||
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SILBuilderWithScope Builder(Inst);
|
SILBuilderWithScope Builder(Inst);
|
||||||
|
|
||||||
if (Feasibility == DynamicCastFeasibility::WillFail) {
|
if (Feasibility == DynamicCastFeasibility::WillFail) {
|
||||||
@@ -1939,39 +1938,45 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) {
|
|||||||
return NewI;
|
return NewI;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Casting will succeed.
|
|
||||||
|
|
||||||
// Replace by unconditional_cast, followed by a branch.
|
|
||||||
// The unconditional_cast can be skipped, if the result of a cast
|
|
||||||
// is not used afterwards.
|
|
||||||
bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty();
|
bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty();
|
||||||
SILValue CastedValue;
|
SILValue CastedValue;
|
||||||
if (Op->getType() != LoweredTargetType) {
|
if (Op->getType() != LoweredTargetType) {
|
||||||
if (!ResultNotUsed) {
|
auto Src = Inst->getOperand();
|
||||||
auto Src = Inst->getOperand();
|
auto Dest = SILValue();
|
||||||
auto Dest = SILValue();
|
// Apply the bridged cast optimizations.
|
||||||
// To apply the bridged casts optimizations.
|
auto BridgedI = optimizeBridgedCasts(Inst,
|
||||||
auto BridgedI = optimizeBridgedCasts(Inst,
|
CastConsumptionKind::CopyOnSuccess, false, Src, Dest, SourceType,
|
||||||
CastConsumptionKind::CopyOnSuccess, false, Src, Dest, SourceType,
|
TargetType, nullptr, nullptr);
|
||||||
TargetType, nullptr, nullptr);
|
|
||||||
|
|
||||||
if (BridgedI) {
|
if (BridgedI) {
|
||||||
CastedValue = BridgedI;
|
CastedValue = BridgedI;
|
||||||
} else {
|
} else {
|
||||||
|
// If the cast may succeed or fail and can't be turned into a bridging
|
||||||
|
// call, then let it be.
|
||||||
|
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
|
||||||
|
|
||||||
|
// Replace by unconditional_cast, followed by a branch.
|
||||||
|
// The unconditional_cast can be skipped, if the result of a cast
|
||||||
|
// is not used afterwards.
|
||||||
|
if (!ResultNotUsed) {
|
||||||
if (!canUseScalarCheckedCastInstructions(Mod, SourceType, TargetType))
|
if (!canUseScalarCheckedCastInstructions(Mod, SourceType, TargetType))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
CastedValue = emitSuccessfulScalarUnconditionalCast(
|
CastedValue = emitSuccessfulScalarUnconditionalCast(
|
||||||
Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType,
|
Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType,
|
||||||
SourceType, TargetType, Inst);
|
SourceType, TargetType, Inst);
|
||||||
|
} else {
|
||||||
|
CastedValue = SILUndef::get(LoweredTargetType, Mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CastedValue)
|
if (!CastedValue)
|
||||||
CastedValue =
|
CastedValue =
|
||||||
Builder.createUnconditionalCheckedCast(Loc, Op, LoweredTargetType);
|
Builder.createUnconditionalCheckedCast(Loc, Op, LoweredTargetType);
|
||||||
} else {
|
|
||||||
CastedValue = SILUndef::get(LoweredTargetType, Mod);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// No need to cast.
|
// No need to cast.
|
||||||
CastedValue = Op;
|
CastedValue = Op;
|
||||||
@@ -2005,10 +2010,6 @@ SILInstruction *CastOptimizer::simplifyCheckedCastValueBranchInst(
|
|||||||
auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), SourceType,
|
auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), SourceType,
|
||||||
TargetType, isSourceTypeExact);
|
TargetType, isSourceTypeExact);
|
||||||
|
|
||||||
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SILBuilderWithScope Builder(Inst);
|
SILBuilderWithScope Builder(Inst);
|
||||||
|
|
||||||
if (Feasibility == DynamicCastFeasibility::WillFail) {
|
if (Feasibility == DynamicCastFeasibility::WillFail) {
|
||||||
@@ -2020,37 +2021,45 @@ SILInstruction *CastOptimizer::simplifyCheckedCastValueBranchInst(
|
|||||||
|
|
||||||
// Casting will succeed.
|
// Casting will succeed.
|
||||||
|
|
||||||
// Replace by unconditional_cast, followed by a branch.
|
|
||||||
// The unconditional_cast can be skipped, if the result of a cast
|
|
||||||
// is not used afterwards.
|
|
||||||
bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty();
|
bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty();
|
||||||
SILValue CastedValue;
|
SILValue CastedValue;
|
||||||
if (Op->getType() != LoweredTargetType) {
|
if (Op->getType() != LoweredTargetType) {
|
||||||
if (!ResultNotUsed) {
|
auto Src = Inst->getOperand();
|
||||||
auto Src = Inst->getOperand();
|
auto Dest = SILValue();
|
||||||
auto Dest = SILValue();
|
// Apply the bridged cast optimizations.
|
||||||
// To apply the bridged casts optimizations.
|
auto BridgedI = optimizeBridgedCasts(
|
||||||
auto BridgedI = optimizeBridgedCasts(
|
Inst, CastConsumptionKind::CopyOnSuccess, false, Src, Dest,
|
||||||
Inst, CastConsumptionKind::CopyOnSuccess, false, Src, Dest,
|
SourceType, TargetType, nullptr, nullptr);
|
||||||
SourceType, TargetType, nullptr, nullptr);
|
|
||||||
|
|
||||||
if (BridgedI) {
|
if (BridgedI) {
|
||||||
CastedValue = BridgedI;
|
CastedValue = BridgedI;
|
||||||
} else {
|
} else {
|
||||||
if (!canUseScalarCheckedCastInstructions(Mod, SourceType, TargetType))
|
// If the cast may succeed or fail and can't be turned into a bridging
|
||||||
return nullptr;
|
// call, then let it be.
|
||||||
|
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
|
||||||
|
|
||||||
|
// Replace by unconditional_cast, followed by a branch.
|
||||||
|
// The unconditional_cast can be skipped, if the result of a cast
|
||||||
|
// is not used afterwards.
|
||||||
|
|
||||||
|
if (!canUseScalarCheckedCastInstructions(Mod, SourceType, TargetType))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!ResultNotUsed) {
|
||||||
CastedValue = emitSuccessfulScalarUnconditionalCast(
|
CastedValue = emitSuccessfulScalarUnconditionalCast(
|
||||||
Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType,
|
Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType,
|
||||||
SourceType, TargetType, Inst);
|
SourceType, TargetType, Inst);
|
||||||
|
} else {
|
||||||
|
CastedValue = SILUndef::get(LoweredTargetType, Mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CastedValue)
|
|
||||||
CastedValue = Builder.createUnconditionalCheckedCastValue(
|
|
||||||
Loc, CastConsumptionKind::TakeAlways, Op, LoweredTargetType);
|
|
||||||
} else {
|
|
||||||
CastedValue = SILUndef::get(LoweredTargetType, Mod);
|
|
||||||
}
|
}
|
||||||
|
if (!CastedValue)
|
||||||
|
CastedValue = Builder.createUnconditionalCheckedCastValue(
|
||||||
|
Loc, CastConsumptionKind::TakeAlways, Op, LoweredTargetType);
|
||||||
} else {
|
} else {
|
||||||
// No need to cast.
|
// No need to cast.
|
||||||
CastedValue = Op;
|
CastedValue = Op;
|
||||||
@@ -2326,10 +2335,6 @@ optimizeUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *Inst) {
|
|||||||
Inst->getTargetType(),
|
Inst->getTargetType(),
|
||||||
isSourceTypeExact);
|
isSourceTypeExact);
|
||||||
|
|
||||||
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Feasibility == DynamicCastFeasibility::WillFail) {
|
if (Feasibility == DynamicCastFeasibility::WillFail) {
|
||||||
// Remove the cast and insert a trap, followed by an
|
// Remove the cast and insert a trap, followed by an
|
||||||
// unreachable instruction.
|
// unreachable instruction.
|
||||||
@@ -2356,44 +2361,52 @@ optimizeUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *Inst) {
|
|||||||
WillSucceedAction();
|
WillSucceedAction();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SILBuilderWithScope Builder(Inst);
|
SILBuilderWithScope Builder(Inst);
|
||||||
|
|
||||||
// Try to apply the bridged casts optimizations
|
// Try to apply the bridged casts optimizations
|
||||||
auto SourceType = LoweredSourceType.getSwiftRValueType();
|
auto SourceType = LoweredSourceType.getSwiftRValueType();
|
||||||
auto TargetType = LoweredTargetType.getSwiftRValueType();
|
auto TargetType = LoweredTargetType.getSwiftRValueType();
|
||||||
auto Src = Inst->getOperand();
|
auto Src = Inst->getOperand();
|
||||||
auto NewI = optimizeBridgedCasts(Inst, CastConsumptionKind::CopyOnSuccess,
|
auto NewI = optimizeBridgedCasts(Inst, CastConsumptionKind::CopyOnSuccess,
|
||||||
false, Src, SILValue(), SourceType,
|
false, Src, SILValue(), SourceType,
|
||||||
TargetType, nullptr, nullptr);
|
TargetType, nullptr, nullptr);
|
||||||
if (NewI) {
|
if (NewI) {
|
||||||
ReplaceInstUsesAction(Inst, NewI);
|
ReplaceInstUsesAction(Inst, NewI);
|
||||||
EraseInstAction(Inst);
|
|
||||||
WillSucceedAction();
|
|
||||||
return NewI;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBridgingCast(SourceType, TargetType))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
auto Result = emitSuccessfulScalarUnconditionalCast(Builder,
|
|
||||||
Mod.getSwiftModule(), Loc, Op,
|
|
||||||
LoweredTargetType,
|
|
||||||
LoweredSourceType.getSwiftRValueType(),
|
|
||||||
LoweredTargetType.getSwiftRValueType(),
|
|
||||||
Inst);
|
|
||||||
|
|
||||||
if (!Result) {
|
|
||||||
// No optimization was possible.
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReplaceInstUsesAction(Inst, Result);
|
|
||||||
EraseInstAction(Inst);
|
EraseInstAction(Inst);
|
||||||
WillSucceedAction();
|
WillSucceedAction();
|
||||||
return Result;
|
return NewI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the cast may succeed or fail and can't be optimized into a bridging
|
||||||
|
// call, let it be.
|
||||||
|
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
|
||||||
|
|
||||||
|
if (isBridgingCast(SourceType, TargetType))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto Result = emitSuccessfulScalarUnconditionalCast(Builder,
|
||||||
|
Mod.getSwiftModule(), Loc, Op,
|
||||||
|
LoweredTargetType,
|
||||||
|
LoweredSourceType.getSwiftRValueType(),
|
||||||
|
LoweredTargetType.getSwiftRValueType(),
|
||||||
|
Inst);
|
||||||
|
|
||||||
|
if (!Result) {
|
||||||
|
// No optimization was possible.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplaceInstUsesAction(Inst, Result);
|
||||||
|
EraseInstAction(Inst);
|
||||||
|
WillSucceedAction();
|
||||||
|
return Result;
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3559,7 +3559,7 @@ ConstraintSystem::simplifyBridgingConstraint(Type type1,
|
|||||||
// We accepted these coercions in Swift 3 mode, so we have to live with
|
// We accepted these coercions in Swift 3 mode, so we have to live with
|
||||||
// them (but give a warning) in that language mode.
|
// them (but give a warning) in that language mode.
|
||||||
if (!TC.Context.LangOpts.isSwiftVersion3()
|
if (!TC.Context.LangOpts.isSwiftVersion3()
|
||||||
&& TC.isObjCClassWithMultipleSwiftBridgedTypes(objcClass, DC))
|
&& TC.Context.isObjCClassWithMultipleSwiftBridgedTypes(objcClass))
|
||||||
return SolutionKind::Error;
|
return SolutionKind::Error;
|
||||||
|
|
||||||
// If the bridged value type is generic, the generic arguments
|
// If the bridged value type is generic, the generic arguments
|
||||||
|
|||||||
@@ -3466,7 +3466,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
|
|||||||
if (Context.LangOpts.isSwiftVersion3() && extraFromOptionals == 0) {
|
if (Context.LangOpts.isSwiftVersion3() && extraFromOptionals == 0) {
|
||||||
// Do the check for a bridging conversion now that we deferred above.
|
// Do the check for a bridging conversion now that we deferred above.
|
||||||
if (isObjCBridgedTo(fromType, toType, dc, &unwrappedIUO) && !unwrappedIUO) {
|
if (isObjCBridgedTo(fromType, toType, dc, &unwrappedIUO) && !unwrappedIUO) {
|
||||||
if (isObjCClassWithMultipleSwiftBridgedTypes(fromType, dc)) {
|
if (Context.isObjCClassWithMultipleSwiftBridgedTypes(fromType)) {
|
||||||
return CheckedCastKind::Swift3BridgingDowncast;
|
return CheckedCastKind::Swift3BridgingDowncast;
|
||||||
}
|
}
|
||||||
return CheckedCastKind::BridgingCoercion;
|
return CheckedCastKind::BridgingCoercion;
|
||||||
|
|||||||
@@ -171,33 +171,6 @@ Type TypeChecker::getNSErrorType(DeclContext *dc) {
|
|||||||
dc);
|
dc);
|
||||||
}
|
}
|
||||||
|
|
||||||
Type TypeChecker::getNSNumberType(DeclContext *dc) {
|
|
||||||
return getObjectiveCNominalType(*this, NSNumberType, Context.Id_Foundation,
|
|
||||||
Context.getSwiftId(
|
|
||||||
KnownFoundationEntity::NSNumber),
|
|
||||||
dc);
|
|
||||||
}
|
|
||||||
|
|
||||||
Type TypeChecker::getNSValueType(DeclContext *dc) {
|
|
||||||
return getObjectiveCNominalType(*this, NSValueType, Context.Id_Foundation,
|
|
||||||
Context.getSwiftId(
|
|
||||||
KnownFoundationEntity::NSValue),
|
|
||||||
dc);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TypeChecker::isObjCClassWithMultipleSwiftBridgedTypes(Type t,
|
|
||||||
DeclContext *dc) {
|
|
||||||
if (auto nsNumber = getNSNumberType(dc)) {
|
|
||||||
if (t->isEqual(nsNumber))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (auto nsValue = getNSValueType(dc)) {
|
|
||||||
if (t->isEqual(nsValue))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type TypeChecker::getObjCSelectorType(DeclContext *dc) {
|
Type TypeChecker::getObjCSelectorType(DeclContext *dc) {
|
||||||
return getObjectiveCNominalType(*this, ObjCSelectorType,
|
return getObjectiveCNominalType(*this, ObjCSelectorType,
|
||||||
Context.Id_ObjectiveC,
|
Context.Id_ObjectiveC,
|
||||||
|
|||||||
@@ -888,14 +888,9 @@ public:
|
|||||||
Type getUInt8Type(DeclContext *dc);
|
Type getUInt8Type(DeclContext *dc);
|
||||||
Type getNSObjectType(DeclContext *dc);
|
Type getNSObjectType(DeclContext *dc);
|
||||||
Type getNSErrorType(DeclContext *dc);
|
Type getNSErrorType(DeclContext *dc);
|
||||||
Type getNSNumberType(DeclContext *dc);
|
|
||||||
Type getNSValueType(DeclContext *dc);
|
|
||||||
Type getObjCSelectorType(DeclContext *dc);
|
Type getObjCSelectorType(DeclContext *dc);
|
||||||
Type getExceptionType(DeclContext *dc, SourceLoc loc);
|
Type getExceptionType(DeclContext *dc, SourceLoc loc);
|
||||||
|
|
||||||
/// True if `t` is an ObjC class that multiple Swift value types bridge into.
|
|
||||||
bool isObjCClassWithMultipleSwiftBridgedTypes(Type t, DeclContext *dc);
|
|
||||||
|
|
||||||
/// \brief Try to resolve an IdentTypeRepr, returning either the referenced
|
/// \brief Try to resolve an IdentTypeRepr, returning either the referenced
|
||||||
/// Type or an ErrorType in case of error.
|
/// Type or an ErrorType in case of error.
|
||||||
Type resolveIdentifierType(DeclContext *DC,
|
Type resolveIdentifierType(DeclContext *DC,
|
||||||
|
|||||||
31
test/SILOptimizer/cast_folding_objc_bridging_conditional.sil
Normal file
31
test/SILOptimizer/cast_folding_objc_bridging_conditional.sil
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-sil %s | %FileCheck %s
|
||||||
|
// REQUIRES: objc_interop
|
||||||
|
|
||||||
|
sil_stage raw
|
||||||
|
|
||||||
|
import Swift
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct MyError: Error {}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil @cast_nserror_to_specific_error
|
||||||
|
sil @cast_nserror_to_specific_error : $@convention(thin) (@in NSError, @in NSNumber, @in NSValue) -> () {
|
||||||
|
entry(%0 : $*NSError, %1 : $*NSNumber, %2 : $*NSValue):
|
||||||
|
%f = function_ref @use : $@convention(thin) <T> (@in T) -> ()
|
||||||
|
%a = alloc_stack $MyError
|
||||||
|
// CHECK: checked_cast_addr_br {{.*}} NSError {{.*}} to MyError
|
||||||
|
checked_cast_addr_br take_always NSError in %0 : $*NSError to MyError in %a : $*MyError, yes, no
|
||||||
|
|
||||||
|
yes:
|
||||||
|
apply %f<MyError>(%a) : $@convention(thin) <T> (@in T) -> ()
|
||||||
|
br done
|
||||||
|
|
||||||
|
no:
|
||||||
|
br done
|
||||||
|
|
||||||
|
done:
|
||||||
|
dealloc_stack %a : $*MyError
|
||||||
|
return undef : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
sil @use : $@convention(thin) <T> (@in T) -> ()
|
||||||
Reference in New Issue
Block a user