Skip to content

[SIL Optimizer] Determine feasibility of dynamic casts between tuple types #4901

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions lib/SIL/DynamicCasts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,40 @@ swift::classifyDynamicCast(Module *M,
}
}

// Tuple casts.
if (auto sourceTuple = dyn_cast<TupleType>(source)) {
if (auto targetTuple = dyn_cast<TupleType>(target)) {
// # of elements must coincide.
if (sourceTuple->getNumElements() != targetTuple->getNumElements())
return DynamicCastFeasibility::WillFail;

DynamicCastFeasibility result = DynamicCastFeasibility::WillSucceed;
for (unsigned i : range(sourceTuple->getNumElements())) {
const auto &sourceElt = sourceTuple->getElement(i);
const auto &targetElt = targetTuple->getElement(i);

// If both have names and the names mismatch, the cast will fail.
if (sourceElt.hasName() && targetElt.hasName() &&
sourceElt.getName() != targetElt.getName())
return DynamicCastFeasibility::WillFail;

// Combine the result of prior elements with this element type.
result = std::max(result,
classifyDynamicCast(M,
sourceElt.getType()->getCanonicalType(),
targetElt.getType()->getCanonicalType(),
isSourceTypeExact,
isWholeModuleOpts));

// If this element failed, we're done.
if (result == DynamicCastFeasibility::WillFail)
break;
}

return result;
}
}

// Class casts.
auto sourceClass = source.getClassOrBoundGenericClass();
auto targetClass = target.getClassOrBoundGenericClass();
Expand Down Expand Up @@ -593,8 +627,6 @@ swift::classifyDynamicCast(Module *M,
// 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?

Expand Down
121 changes: 121 additions & 0 deletions test/SILOptimizer/cast_folding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ protocol P {}
protocol Q: P {}

class A:P {}
class AA:A {}

class X {}

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

func cast38<T>(_ t: T) -> Bool {
return t is (Int, Int)
}

func cast39<T>(_ t: T) -> Bool {
return t is (x: Int, y: Int)
}

func cast40<T>(_ t: T) -> Bool {
return t is (x: Int, y: A)
}

// CHECK-LABEL: sil hidden [noinline] @_TF12cast_folding5test0FT_Sb : $@convention(thin) () -> Bool
// CHECK: bb0
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
Expand Down Expand Up @@ -798,6 +811,114 @@ public func test37<T>(ah: T) {
}
}

// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test38a
// CHECK: bb0
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
// CHECK-NEXT: %1 = struct $Bool
// CHECK-NEXT: return %1
@inline(never)
public func test38a() -> Bool {
return cast38((1, 2))
}

// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test38b
// CHECK: bb0
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
// CHECK-NEXT: %1 = struct $Bool
// CHECK-NEXT: return %1
@inline(never)
public func test38b() -> Bool {
return cast38((x: 1, y: 2))
}

// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test38c
// CHECK: bb0
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
// CHECK-NEXT: %1 = struct $Bool
// CHECK-NEXT: return %1
@inline(never)
public func test38c() -> Bool {
return cast38((z: 1, y: 2))
}

// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test39a
// CHECK: bb0
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
// CHECK-NEXT: %1 = struct $Bool
// CHECK-NEXT: return %1
@inline(never)
public func test39a() -> Bool {
return cast39((1, 2))
}

// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test39b
// CHECK: bb0
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
// CHECK-NEXT: %1 = struct $Bool
// CHECK-NEXT: return %1
@inline(never)
public func test39b() -> Bool {
return cast39((x: 1, y: 2))
}

// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test39c
// CHECK: bb0
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, 0
// CHECK-NEXT: %1 = struct $Bool
// CHECK-NEXT: return %1
@inline(never)
public func test39c() -> Bool {
return cast39((z: 1, y: 2))
}

// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test39d
// CHECK: bb0
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, 0
// CHECK-NEXT: %1 = struct $Bool
// CHECK-NEXT: return %1
@inline(never)
public func test39d() -> Bool {
return cast39((1, 2, 3))
}

// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test40a
// CHECK: bb0
// FIXME: Would love to fold this to just "true"
// CHECK-NOT: return:
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func test40a() -> Bool {
return cast40((1, A()))
}

// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test40b
// CHECK: bb0
// FIXME: Would love to fold this to just "true"
// CHECK-NOT: return:
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func test40b() -> Bool {
return cast40((1, AA()))
}

// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test40c
// CHECK: bb0
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, 0
// CHECK-NEXT: %1 = struct $Bool
// CHECK-NEXT: return %1
@inline(never)
public func test40c() -> Bool {
return cast40((1, S()))
}

// CHECK-LABEL: sil [noinline] @_TF12cast_folding7test40d
// CHECK: bb0
// CHECK-NOT: return
// CHECK: checked_cast_addr_br take_always (Int, Any) in
@inline(never)
public func test40d(_ a: Any) -> Bool {
return cast40((1, a))
}

print("test0=\(test0())")

Expand Down