Skip to content

Commit 8da7d7c

Browse files
committed
[SIL Optimizer] Determine feasibility of dynamic casts between tuple types.
The SIL optimizer logic that determined feasability of dynamic casts completely ignored tuple types, therefore assuming that they would always fail. Check for structural identity, ignoring adding/removing labels. Fixes rdar://problem/28121915.
1 parent 1a81898 commit 8da7d7c

File tree

2 files changed

+155
-2
lines changed

2 files changed

+155
-2
lines changed

lib/SIL/DynamicCasts.cpp

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,40 @@ swift::classifyDynamicCast(Module *M,
481481
}
482482
}
483483

484+
// Tuple casts.
485+
if (auto sourceTuple = dyn_cast<TupleType>(source)) {
486+
if (auto targetTuple = dyn_cast<TupleType>(target)) {
487+
// # of elements must coincide.
488+
if (sourceTuple->getNumElements() != targetTuple->getNumElements())
489+
return DynamicCastFeasibility::WillFail;
490+
491+
DynamicCastFeasibility result = DynamicCastFeasibility::WillSucceed;
492+
for (unsigned i : range(sourceTuple->getNumElements())) {
493+
const auto &sourceElt = sourceTuple->getElement(i);
494+
const auto &targetElt = targetTuple->getElement(i);
495+
496+
// If both have names and the names mismatch, the cast will fail.
497+
if (sourceElt.hasName() && targetElt.hasName() &&
498+
sourceElt.getName() != targetElt.getName())
499+
return DynamicCastFeasibility::WillFail;
500+
501+
// Combine the result of prior elements with this element type.
502+
result = std::max(result,
503+
classifyDynamicCast(M,
504+
sourceElt.getType()->getCanonicalType(),
505+
targetElt.getType()->getCanonicalType(),
506+
isSourceTypeExact,
507+
isWholeModuleOpts));
508+
509+
// If this element failed, we're done.
510+
if (result == DynamicCastFeasibility::WillFail)
511+
break;
512+
}
513+
514+
return result;
515+
}
516+
}
517+
484518
// Class casts.
485519
auto sourceClass = source.getClassOrBoundGenericClass();
486520
auto targetClass = target.getClassOrBoundGenericClass();
@@ -575,8 +609,6 @@ swift::classifyDynamicCast(Module *M,
575609
// succeed.
576610
if (target->is<AnyMetatypeType>()) return DynamicCastFeasibility::WillFail;
577611

578-
// FIXME: tuple conversions?
579-
580612
// FIXME: Be more careful with bridging conversions from
581613
// NSArray, NSDictionary and NSSet as they may fail?
582614

test/SILOptimizer/cast_folding.swift

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ protocol P {}
1111
protocol Q: P {}
1212

1313
class A:P {}
14+
class AA:A {}
1415

1516
class X {}
1617

@@ -204,6 +205,18 @@ func cast33(_ p: P) -> Bool {
204205
return p is X.Type
205206
}
206207

208+
func cast38<T>(_ t: T) -> Bool {
209+
return t is (Int, Int)
210+
}
211+
212+
func cast39<T>(_ t: T) -> Bool {
213+
return t is (x: Int, y: Int)
214+
}
215+
216+
func cast40<T>(_ t: T) -> Bool {
217+
return t is (x: Int, y: A)
218+
}
219+
207220
// CHECK-LABEL: sil hidden [noinline] @_TF12cast_folding5test0FT_Sb : $@convention(thin) () -> Bool
208221
// CHECK: bb0
209222
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
@@ -798,6 +811,114 @@ public func test37<T>(ah: T) {
798811
}
799812
}
800813

814+
// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test38a
815+
// CHECK: bb0
816+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
817+
// CHECK-NEXT: %1 = struct $Bool
818+
// CHECK-NEXT: return %1
819+
@inline(never)
820+
public func test38a() -> Bool {
821+
return cast38((1, 2))
822+
}
823+
824+
// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test38b
825+
// CHECK: bb0
826+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
827+
// CHECK-NEXT: %1 = struct $Bool
828+
// CHECK-NEXT: return %1
829+
@inline(never)
830+
public func test38b() -> Bool {
831+
return cast38((x: 1, y: 2))
832+
}
833+
834+
// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test38c
835+
// CHECK: bb0
836+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
837+
// CHECK-NEXT: %1 = struct $Bool
838+
// CHECK-NEXT: return %1
839+
@inline(never)
840+
public func test38c() -> Bool {
841+
return cast38((z: 1, y: 2))
842+
}
843+
844+
// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test39a
845+
// CHECK: bb0
846+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
847+
// CHECK-NEXT: %1 = struct $Bool
848+
// CHECK-NEXT: return %1
849+
@inline(never)
850+
public func test39a() -> Bool {
851+
return cast39((1, 2))
852+
}
853+
854+
// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test39b
855+
// CHECK: bb0
856+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
857+
// CHECK-NEXT: %1 = struct $Bool
858+
// CHECK-NEXT: return %1
859+
@inline(never)
860+
public func test39b() -> Bool {
861+
return cast39((x: 1, y: 2))
862+
}
863+
864+
// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test39c
865+
// CHECK: bb0
866+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, 0
867+
// CHECK-NEXT: %1 = struct $Bool
868+
// CHECK-NEXT: return %1
869+
@inline(never)
870+
public func test39c() -> Bool {
871+
return cast39((z: 1, y: 2))
872+
}
873+
874+
// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test39d
875+
// CHECK: bb0
876+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, 0
877+
// CHECK-NEXT: %1 = struct $Bool
878+
// CHECK-NEXT: return %1
879+
@inline(never)
880+
public func test39d() -> Bool {
881+
return cast39((1, 2, 3))
882+
}
883+
884+
// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test40a
885+
// CHECK: bb0
886+
// FIXME: Would love to fold this to just "true"
887+
// CHECK-NOT: return:
888+
// CHECK: unconditional_checked_cast_addr
889+
@inline(never)
890+
public func test40a() -> Bool {
891+
return cast40((1, A()))
892+
}
893+
894+
// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test40b
895+
// CHECK: bb0
896+
// FIXME: Would love to fold this to just "true"
897+
// CHECK-NOT: return:
898+
// CHECK: unconditional_checked_cast_addr
899+
@inline(never)
900+
public func test40b() -> Bool {
901+
return cast40((1, AA()))
902+
}
903+
904+
// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test40c
905+
// CHECK: bb0
906+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, 0
907+
// CHECK-NEXT: %1 = struct $Bool
908+
// CHECK-NEXT: return %1
909+
@inline(never)
910+
public func test40c() -> Bool {
911+
return cast40((1, S()))
912+
}
913+
914+
// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test40d
915+
// CHECK: bb0
916+
// CHECK-NOT: return
917+
// CHECK: checked_cast_addr_br take_always (Int, Any) in
918+
@inline(never)
919+
public func test40d(_ a: Any) -> Bool {
920+
return cast40((1, a))
921+
}
801922

802923
print("test0=\(test0())")
803924

0 commit comments

Comments
 (0)