Skip to content

Commit 0ca72a4

Browse files
authored
Merge pull request #4901 from DougGregor/opt-tuple-cast-feasibility-3-0
[SIL Optimizer] Determine feasibility of dynamic casts between tuple types
2 parents 1f11ef2 + 6b26ba5 commit 0ca72a4

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
@@ -499,6 +499,40 @@ swift::classifyDynamicCast(Module *M,
499499
}
500500
}
501501

502+
// Tuple casts.
503+
if (auto sourceTuple = dyn_cast<TupleType>(source)) {
504+
if (auto targetTuple = dyn_cast<TupleType>(target)) {
505+
// # of elements must coincide.
506+
if (sourceTuple->getNumElements() != targetTuple->getNumElements())
507+
return DynamicCastFeasibility::WillFail;
508+
509+
DynamicCastFeasibility result = DynamicCastFeasibility::WillSucceed;
510+
for (unsigned i : range(sourceTuple->getNumElements())) {
511+
const auto &sourceElt = sourceTuple->getElement(i);
512+
const auto &targetElt = targetTuple->getElement(i);
513+
514+
// If both have names and the names mismatch, the cast will fail.
515+
if (sourceElt.hasName() && targetElt.hasName() &&
516+
sourceElt.getName() != targetElt.getName())
517+
return DynamicCastFeasibility::WillFail;
518+
519+
// Combine the result of prior elements with this element type.
520+
result = std::max(result,
521+
classifyDynamicCast(M,
522+
sourceElt.getType()->getCanonicalType(),
523+
targetElt.getType()->getCanonicalType(),
524+
isSourceTypeExact,
525+
isWholeModuleOpts));
526+
527+
// If this element failed, we're done.
528+
if (result == DynamicCastFeasibility::WillFail)
529+
break;
530+
}
531+
532+
return result;
533+
}
534+
}
535+
502536
// Class casts.
503537
auto sourceClass = source.getClassOrBoundGenericClass();
504538
auto targetClass = target.getClassOrBoundGenericClass();
@@ -593,8 +627,6 @@ swift::classifyDynamicCast(Module *M,
593627
// succeed.
594628
if (target->is<AnyMetatypeType>()) return DynamicCastFeasibility::WillFail;
595629

596-
// FIXME: tuple conversions?
597-
598630
// FIXME: Be more careful with bridging conversions from
599631
// NSArray, NSDictionary and NSSet as they may fail?
600632

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)