Skip to content

Commit 19c51e2

Browse files
authored
Merge pull request #79872 from WeZZard/bugfix/unchecked-ref-casting-with-optional-any-object
[SILOptimizer] Fixes the ValueDefUseWalker to handle potential unchecked_ref_cast between optional and non-optional class types.
2 parents 4949e2e + 4c47982 commit 19c51e2

File tree

10 files changed

+125
-3
lines changed

10 files changed

+125
-3
lines changed

SwiftCompilerSources/Sources/AST/Type.swift

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ extension TypeProperties {
9393
public var isEscapable: Bool { type.bridged.isEscapable() }
9494
public var isNoEscape: Bool { type.bridged.isNoEscape() }
9595
public var isInteger: Bool { type.bridged.isInteger() }
96+
public var isOptional: Bool { type.bridged.isOptional() }
9697
public var isMetatypeType: Bool { type.bridged.isMetatypeType() }
9798
public var isExistentialMetatypeType: Bool { type.bridged.isExistentialMetatypeType() }
9899
public var representationOfMetatype: AST.`Type`.MetatypeRepresentation {

SwiftCompilerSources/Sources/Optimizer/TestPasses/EscapeInfoDumper.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import SIL
1414

1515
/// Dumps the results of escape analysis.
1616
///
17-
/// Dumps the EscapeInfo query results for all `alloc_stack` instructions in a function.
17+
/// Dumps the EscapeInfo query results for all `alloc_ref` instructions in a function.
1818
///
1919
/// This pass is used for testing EscapeInfo.
2020
let escapeInfoDumper = FunctionPass(name: "dump-escape-info") {

SwiftCompilerSources/Sources/SIL/Type.swift

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public struct Type : CustomStringConvertible, NoReflectionChildren {
6363
public var isFunction: Bool { bridged.isFunction() }
6464
public var isMetatype: Bool { bridged.isMetatype() }
6565
public var isClassExistential: Bool { bridged.isClassExistential() }
66+
public var isOptional: Bool { astType.isOptional }
6667
public var isNoEscapeFunction: Bool { bridged.isNoEscapeFunction() }
6768
public var containsNoEscapeFunction: Bool { bridged.containsNoEscapeFunction() }
6869
public var isThickFunction: Bool { bridged.isThickFunction() }

SwiftCompilerSources/Sources/SIL/Utilities/WalkUtils.swift

+30-2
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,21 @@ extension ValueDefUseWalker {
370370
// We need to ignore this because otherwise the path wouldn't contain the right `existential` field kind.
371371
return leafUse(value: operand, path: path)
372372
}
373-
return walkDownUses(ofValue: urc, path: path)
373+
// The `unchecked_ref_cast` is designed to be able to cast between
374+
// `Optional<ClassType>` and `ClassType`. We need to handle these
375+
// cases by checking if the type is optional and adjust the path
376+
// accordingly.
377+
switch (urc.type.isOptional, urc.fromInstance.type.isOptional) {
378+
case (true, false):
379+
return walkDownUses(ofValue: urc, path: path.push(.enumCase, index: 1))
380+
case (false, true):
381+
if let path = path.popIfMatches(.enumCase, index: 1) {
382+
return walkDownUses(ofValue: urc, path: path)
383+
}
384+
return unmatchedPath(value: operand, path: path)
385+
default:
386+
return walkDownUses(ofValue: urc, path: path)
387+
}
374388
case let beginDealloc as BeginDeallocRefInst:
375389
if operand.index == 0 {
376390
return walkDownUses(ofValue: beginDealloc, path: path)
@@ -699,7 +713,21 @@ extension ValueUseDefWalker {
699713
// We need to ignore this because otherwise the path wouldn't contain the right `existential` field kind.
700714
return rootDef(value: urc, path: path)
701715
}
702-
return walkUp(value: urc.fromInstance, path: path)
716+
// The `unchecked_ref_cast` is designed to be able to cast between
717+
// `Optional<ClassType>` and `ClassType`. We need to handle these
718+
// cases by checking if the type is optional and adjust the path
719+
// accordingly.
720+
switch (urc.type.isOptional, urc.fromInstance.type.isOptional) {
721+
case (true, false):
722+
if let path = path.popIfMatches(.enumCase, index: 1) {
723+
return walkUp(value: urc.fromInstance, path: path)
724+
}
725+
return unmatchedPath(value: urc.fromInstance, path: path)
726+
case (false, true):
727+
return walkUp(value: urc.fromInstance, path: path.push(.enumCase, index: 1))
728+
default:
729+
return walkUp(value: urc.fromInstance, path: path)
730+
}
703731
case let arg as Argument:
704732
if let phi = Phi(arg) {
705733
for incoming in phi.incomingValues {

include/swift/AST/ASTBridging.h

+1
Original file line numberDiff line numberDiff line change
@@ -3023,6 +3023,7 @@ struct BridgedASTType {
30233023
BRIDGED_INLINE bool isInteger() const;
30243024
BRIDGED_INLINE bool isMetatypeType() const;
30253025
BRIDGED_INLINE bool isExistentialMetatypeType() const;
3026+
BRIDGED_INLINE bool isOptional() const;
30263027
BRIDGED_INLINE TraitResult canBeClass() const;
30273028
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getAnyNominal() const;
30283029
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType getInstanceTypeOfMetatype() const;

include/swift/AST/ASTBridgingImpl.h

+4
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,10 @@ bool BridgedASTType::isMetatypeType() const {
430430
return unbridged()->is<swift::AnyMetatypeType>();
431431
}
432432

433+
bool BridgedASTType::isOptional() const {
434+
return unbridged()->getCanonicalType()->isOptional();
435+
}
436+
433437
bool BridgedASTType::isExistentialMetatypeType() const {
434438
return unbridged()->is<swift::ExistentialMetatypeType>();
435439
}

include/swift/SIL/SILBridging.h

+1
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ struct BridgedType {
263263
BRIDGED_INLINE bool isFunction() const;
264264
BRIDGED_INLINE bool isMetatype() const;
265265
BRIDGED_INLINE bool isClassExistential() const;
266+
BRIDGED_INLINE bool isOptional() const;
266267
BRIDGED_INLINE bool isNoEscapeFunction() const;
267268
BRIDGED_INLINE bool containsNoEscapeFunction() const;
268269
BRIDGED_INLINE bool isThickFunction() const;

include/swift/SIL/SILBridgingImpl.h

+5
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,11 @@ bool BridgedType::isClassExistential() const {
402402
return unbridged().isClassExistentialType();
403403
}
404404

405+
bool BridgedType::isOptional() const {
406+
swift::CanType astType = unbridged().getASTType();
407+
return astType->isOptional();
408+
}
409+
405410
bool BridgedType::isNoEscapeFunction() const {
406411
return unbridged().isNoEscapeFunction();
407412
}

test/SILOptimizer/escape_info.sil

+39
Original file line numberDiff line numberDiff line change
@@ -1458,6 +1458,45 @@ bb0:
14581458
return %2 : $X
14591459
}
14601460

1461+
// CHECK-LABEL: Escape information for test_unchecked_ref_cast_from_optional_to_non_optional:
1462+
// CHECK: return[]: %0 = alloc_ref $Derived
1463+
// CHECK: - : %2 = alloc_ref $Derived
1464+
// CHECK: End function test_unchecked_ref_cast_from_optional_to_non_optional
1465+
sil @test_unchecked_ref_cast_from_optional_to_non_optional : $@convention(thin) () -> @owned X {
1466+
bb0:
1467+
%0 = alloc_ref $Derived
1468+
%1 = enum $Optional<Derived>, #Optional.some!enumelt, %0 : $Derived
1469+
%2 = alloc_ref $Derived
1470+
%3 = enum $Optional<Derived>, #Optional.some!enumelt, %2 : $Derived
1471+
%4 = unchecked_ref_cast %1 : $Optional<Derived> to $X
1472+
%5 = unchecked_ref_cast %3 : $Optional<Derived> to $X
1473+
return %4 : $X
1474+
}
1475+
1476+
// CHECK-LABEL: Escape information for test_unchecked_ref_cast_from_optional_to_non_optional_2:
1477+
// CHECK-NEXT: End function test_unchecked_ref_cast_from_optional_to_non_optional_2
1478+
sil @test_unchecked_ref_cast_from_optional_to_non_optional_2 : $@convention(thin) () -> @owned X {
1479+
bb0:
1480+
%0 = enum $Optional<Derived>, #Optional.none!enumelt
1481+
%1 = enum $Optional<Derived>, #Optional.none!enumelt
1482+
%2 = unchecked_ref_cast %0 : $Optional<Derived> to $X
1483+
%3 = unchecked_ref_cast %1 : $Optional<Derived> to $X
1484+
return %2 : $X
1485+
}
1486+
1487+
// CHECK-LABEL: Escape information for test_unchecked_ref_cast_from_non_optional_to_optional:
1488+
// CHECK: return[e1]: %0 = alloc_ref $Derived
1489+
// CHECK: - : %1 = alloc_ref $Derived
1490+
// CHECK: End function test_unchecked_ref_cast_from_non_optional_to_optional
1491+
sil @test_unchecked_ref_cast_from_non_optional_to_optional : $@convention(thin) () -> @owned Optional<X> {
1492+
bb0:
1493+
%0 = alloc_ref $Derived
1494+
%1 = alloc_ref $Derived
1495+
%2 = unchecked_ref_cast %0 : $Derived to $Optional<X>
1496+
%3 = unchecked_ref_cast %1 : $Derived to $Optional<X>
1497+
return %2 : $Optional<X>
1498+
}
1499+
14611500
// CHECK-LABEL: Escape information for test_unchecked_addr_cast:
14621501
// CHECK: global: %1 = alloc_ref $X
14631502
// CHECK: End function test_unchecked_addr_cast

test/SILOptimizer/redundant_load_elim_with_casts.sil

+42
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ sil @use : $@convention(thin) (Builtin.Int32) -> ()
5252
sil @use_a : $@convention(thin) (@in A) -> ()
5353
sil @escaped_a_ptr : $@convention(thin) () -> @out A
5454
sil @escaped_a : $@convention(thin) () -> Builtin.RawPointer
55+
sil @b_i_plus_one : $@convention(method) (@guaranteed B) -> ()
5556

5657
// *NOTE* This does not handle raw pointer since raw pointer is only layout compatible with heap references.
5758
// CHECK-FUTURE: sil @store_to_load_forward_unchecked_addr_cast_struct : $@convention(thin) (Optional<A>) -> () {
@@ -547,3 +548,44 @@ bb0(%0 : $*(I32, I32, I32), %1 : $*((I32, I32), I32)):
547548
%63 = tuple (%60 : $(I32, I32), %61 : $(I32, I32), %62 : $(I32, I32, I32))
548549
return %63 : $((I32, I32), (I32, I32), (I32, I32, I32))
549550
}
551+
552+
// Tests unchecked_ref_cast between Optional<ClassType1> and ClassType2.
553+
// E? -> B is safe
554+
//
555+
// CHECK-FUTURE: sil @unchecked_ref_cast_from_optional_class
556+
// CHECK: bb3(%6 : $Optional<AnyObject>):
557+
// CHECK: %8 = load %7 : $*Builtin.Int32
558+
// CHECK: %10 = apply %9(%5) : $@convention(method) (@guaranteed B) -> ()
559+
// CHECK: %12 = load %11 : $*Builtin.Int32
560+
// CHECK: return
561+
sil @unchecked_ref_cast_from_optional_class : $@convention(thin) (Optional<E>) -> () {
562+
bb0(%0 : $Optional<E>):
563+
switch_enum %0 : $Optional<E>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
564+
565+
bb1(%1 : $E):
566+
%2 = enum $Optional<E>, #Optional.some!enumelt, %1 : $E
567+
br bb3(%2 : $Optional<E>)
568+
569+
bb2:
570+
%3 = enum $Optional<E>, #Optional.none!enumelt
571+
br bb3(%3 : $Optional<E>)
572+
573+
bb3(%4 : $Optional<E>):
574+
%5 = unchecked_ref_cast %4 : $Optional<E> to $B
575+
576+
%6 = ref_element_addr %5 : $B, #B.i
577+
%7 = begin_access [read] [dynamic] [no_nested_conflict] %6 : $* Builtin.Int32
578+
%8 = load %7 : $*Builtin.Int32
579+
end_access %7 : $*Builtin.Int32
580+
581+
%9 = function_ref @b_i_plus_one : $@convention(method) (@guaranteed B) -> ()
582+
%10 = apply %9(%5) : $@convention(method) (@guaranteed B) -> ()
583+
584+
%11 = begin_access [read] [dynamic] [no_nested_conflict] %6 : $*Builtin.Int32
585+
%12 = load %11 : $*Builtin.Int32
586+
end_access %11 : $*Builtin.Int32
587+
588+
release_value %4 : $Optional<E>
589+
%13 = tuple ()
590+
return %13 : $()
591+
}

0 commit comments

Comments
 (0)