diff --git a/lib/SILOptimizer/Utils/Local.cpp b/lib/SILOptimizer/Utils/Local.cpp index 4fceeb138cd..9469e5d2d6d 100644 --- a/lib/SILOptimizer/Utils/Local.cpp +++ b/lib/SILOptimizer/Utils/Local.cpp @@ -1571,6 +1571,40 @@ optimizeBridgedObjCToSwiftCast(SILInstruction *Inst, return (NewI) ? NewI : AI; } +static bool canOptimizeCast(const swift::Type &BridgedTargetTy, + swift::SILModule &M, + swift::SILFunctionConventions &substConv) { + // DestTy is the type which we want to convert to + SILType DestTy = + SILType::getPrimitiveObjectType(BridgedTargetTy->getCanonicalType()); + // ConvTy is the return type of the _bridgeToObjectiveCImpl() + auto ConvTy = substConv.getSILResultType().getObjectType(); + if (ConvTy == DestTy) { + // Destination is the same type + return true; + } + // Check if a superclass/subclass of the source operand + if (DestTy.isExactSuperclassOf(ConvTy)) { + return true; + } + if (ConvTy.isExactSuperclassOf(DestTy)) { + return true; + } + // check if it is a bridgeable CF type + if (ConvTy.getSwiftRValueType() == + getNSBridgedClassOfCFClass(M.getSwiftModule(), + DestTy.getSwiftRValueType())) { + return true; + } + if (DestTy.getSwiftRValueType() == + getNSBridgedClassOfCFClass(M.getSwiftModule(), + ConvTy.getSwiftRValueType())) { + return true; + } + // All else failed - can't optimize this case + return false; +} + /// Create a call of _bridgeToObjectiveC which converts an _ObjectiveCBridgeable /// instance into a bridged ObjC type. SILInstruction * @@ -1646,7 +1680,8 @@ optimizeBridgedSwiftToObjCCast(SILInstruction *Inst, if (ResultsRef.size() != 1) return nullptr; - auto MemberDeclRef = SILDeclRef(Results.front()); + auto *resultDecl = Results.front(); + auto MemberDeclRef = SILDeclRef(resultDecl); auto *BridgedFunc = M.getOrCreateFunction(Loc, MemberDeclRef, ForDefinition_t::NotForDefinition); @@ -1677,6 +1712,11 @@ optimizeBridgedSwiftToObjCCast(SILInstruction *Inst, SILType SubstFnTy = SILFnTy.substGenericArgs(M, SubMap); SILFunctionConventions substConv(SubstFnTy.castTo(), M); + // check that we can go through with the optimization + if (!canOptimizeCast(BridgedTargetTy, M, substConv)) { + return nullptr; + } + auto FnRef = Builder.createFunctionRef(Loc, BridgedFunc); if (Src->getType().isAddress() && !substConv.isSILIndirect(ParamTypes[0])) { // Create load @@ -1811,6 +1851,9 @@ optimizeBridgedSwiftToObjCCast(SILInstruction *Inst, // If it is addr cast then store the result. auto ConvTy = NewAI->getType(); auto DestTy = Dest->getType().getObjectType(); + assert(DestTy == SILType::getPrimitiveObjectType( + BridgedTargetTy->getCanonicalType()) && + "Expected Dest Type to be the same as BridgedTargetTy"); SILValue CastedValue; if (ConvTy == DestTy) { CastedValue = NewAI; @@ -1842,9 +1885,11 @@ optimizeBridgedSwiftToObjCCast(SILInstruction *Inst, CastedValue = SILValue(Builder.createUncheckedRefCast(Loc, NewAI, DestTy)); } else { - llvm_unreachable( - "Destination should have the same type, be bridgeable CF " - "type or be a superclass/subclass of the source operand"); + llvm_unreachable("optimizeBridgedSwiftToObjCCast: should never reach " + "this condition: if the Destination does not have the " + "same type, is not a bridgeable CF type and isn't a " + "superclass/subclass of the source operand we should " + "have bailed earlier"); } NewI = Builder.createStore(Loc, CastedValue, Dest, StoreOwnershipQualifier::Unqualified); diff --git a/test/SILOptimizer/bridged_casts_folding.swift b/test/SILOptimizer/bridged_casts_folding.swift index 473fb6aa6be..233c2c540f5 100644 --- a/test/SILOptimizer/bridged_casts_folding.swift +++ b/test/SILOptimizer/bridged_casts_folding.swift @@ -911,3 +911,36 @@ var anyHashable: AnyHashable = 0 public func testUncondCastSwiftToSubclass() -> NSObjectSubclass { return anyHashable as! NSObjectSubclass } + +class MyThing: Hashable { + let name: String + + init(name: String) { + self.name = name + } + + deinit { + Swift.print("Deinit \(name)") + } + + var hashValue: Int { + return 0 + } + + static func ==(lhs: MyThing, rhs: MyThing) -> Bool { + return false + } +} + +// CHECK-LABEL: sil hidden [noinline] @_T021bridged_casts_folding26doSomethingWithAnyHashableys0gH0VF : $@convention(thin) (@in AnyHashable) -> () +// CHECK: checked_cast_addr_br take_always AnyHashable in %0 : $*AnyHashable to MyThing +@inline(never) +func doSomethingWithAnyHashable(_ item: AnyHashable) { + _ = item as? MyThing +} + +@inline(never) +public func testMyThing() { + let x = MyThing(name: "B") + doSomethingWithAnyHashable(x) +}