mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge pull request #75434 from eeckstein/fix-walkutils
WalkUtils: handle `unsafe_ref_cast` which casts between AnyObject and a class
This commit is contained in:
@@ -56,6 +56,7 @@ public struct Type : CustomStringConvertible, NoReflectionChildren {
|
||||
public var isEnum: Bool { bridged.isEnumOrBoundGenericEnum() }
|
||||
public var isFunction: Bool { bridged.isFunction() }
|
||||
public var isMetatype: Bool { bridged.isMetatype() }
|
||||
public var isClassExistential: Bool { bridged.isClassExistential() }
|
||||
public var isNoEscapeFunction: Bool { bridged.isNoEscapeFunction() }
|
||||
public var containsNoEscapeFunction: Bool { bridged.containsNoEscapeFunction() }
|
||||
public var isThickFunction: Bool { bridged.isThickFunction() }
|
||||
|
||||
@@ -360,9 +360,17 @@ extension ValueDefUseWalker {
|
||||
return unmatchedPath(value: operand, path: path)
|
||||
}
|
||||
case is BeginBorrowInst, is CopyValueInst, is MoveValueInst,
|
||||
is UpcastInst, is UncheckedRefCastInst, is EndCOWMutationInst, is EndInitLetRefInst,
|
||||
is UpcastInst, is EndCOWMutationInst, is EndInitLetRefInst,
|
||||
is RefToBridgeObjectInst, is BridgeObjectToRefInst, is MarkUnresolvedNonCopyableValueInst:
|
||||
return walkDownUses(ofValue: (instruction as! SingleValueInstruction), path: path)
|
||||
case let urc as UncheckedRefCastInst:
|
||||
if urc.type.isClassExistential || urc.fromInstance.type.isClassExistential {
|
||||
// Sometimes `unchecked_ref_cast` is misused to cast between AnyObject and a class (instead of
|
||||
// init_existential_ref and open_existential_ref).
|
||||
// We need to ignore this because otherwise the path wouldn't contain the right `existential` field kind.
|
||||
return leafUse(value: operand, path: path)
|
||||
}
|
||||
return walkDownUses(ofValue: urc, path: path)
|
||||
case let beginDealloc as BeginDeallocRefInst:
|
||||
if operand.index == 0 {
|
||||
return walkDownUses(ofValue: beginDealloc, path: path)
|
||||
@@ -680,10 +688,18 @@ extension ValueUseDefWalker {
|
||||
case let oer as OpenExistentialRefInst:
|
||||
return walkUp(value: oer.existential, path: path.push(.existential, index: 0))
|
||||
case is BeginBorrowInst, is CopyValueInst, is MoveValueInst,
|
||||
is UpcastInst, is UncheckedRefCastInst, is EndCOWMutationInst, is EndInitLetRefInst,
|
||||
is UpcastInst, is EndCOWMutationInst, is EndInitLetRefInst,
|
||||
is BeginDeallocRefInst,
|
||||
is RefToBridgeObjectInst, is BridgeObjectToRefInst, is MarkUnresolvedNonCopyableValueInst:
|
||||
return walkUp(value: (def as! Instruction).operands[0].value, path: path)
|
||||
case let urc as UncheckedRefCastInst:
|
||||
if urc.type.isClassExistential || urc.fromInstance.type.isClassExistential {
|
||||
// Sometimes `unchecked_ref_cast` is misused to cast between AnyObject and a class (instead of
|
||||
// init_existential_ref and open_existential_ref).
|
||||
// We need to ignore this because otherwise the path wouldn't contain the right `existential` field kind.
|
||||
return rootDef(value: urc, path: path)
|
||||
}
|
||||
return walkUp(value: urc.fromInstance, path: path)
|
||||
case let arg as Argument:
|
||||
if let phi = Phi(arg) {
|
||||
for incoming in phi.incomingValues {
|
||||
|
||||
@@ -400,6 +400,7 @@ struct BridgedType {
|
||||
BRIDGED_INLINE bool isEnumOrBoundGenericEnum() const;
|
||||
BRIDGED_INLINE bool isFunction() const;
|
||||
BRIDGED_INLINE bool isMetatype() const;
|
||||
BRIDGED_INLINE bool isClassExistential() const;
|
||||
BRIDGED_INLINE bool isNoEscapeFunction() const;
|
||||
BRIDGED_INLINE bool containsNoEscapeFunction() const;
|
||||
BRIDGED_INLINE bool isThickFunction() const;
|
||||
|
||||
@@ -296,6 +296,10 @@ bool BridgedType::isMetatype() const {
|
||||
return unbridged().isMetatype();
|
||||
}
|
||||
|
||||
bool BridgedType::isClassExistential() const {
|
||||
return unbridged().isClassExistentialType();
|
||||
}
|
||||
|
||||
bool BridgedType::isNoEscapeFunction() const {
|
||||
return unbridged().isNoEscapeFunction();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ class X {
|
||||
@_hasStorage var s: Str
|
||||
}
|
||||
|
||||
class D: X {}
|
||||
|
||||
struct Container {
|
||||
@_hasStorage var x: X
|
||||
}
|
||||
@@ -755,3 +757,42 @@ bb0:
|
||||
return %8 : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: Address escape information for test_unchecked_ref_cast:
|
||||
// CHECK: pair 0 - 1
|
||||
// CHECK-NEXT: %4 = ref_element_addr %2 : $X, #X.s
|
||||
// CHECK-NEXT: %6 = ref_element_addr %3 : $X, #X.s
|
||||
// CHECK-NEXT: no alias
|
||||
// CHECK: End function test_unchecked_ref_cast
|
||||
sil @test_unchecked_ref_cast : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%0 = alloc_ref $D
|
||||
%1 = alloc_ref $D
|
||||
%2 = unchecked_ref_cast %0 : $D to $X
|
||||
%3 = unchecked_ref_cast %1 : $D to $X
|
||||
%4 = ref_element_addr %2 : $X, #X.s
|
||||
fix_lifetime %4 : $*Str
|
||||
%6 = ref_element_addr %3 : $X, #X.s
|
||||
fix_lifetime %6 : $*Str
|
||||
%r = tuple ()
|
||||
return %r : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: Address escape information for test_anyobject_cast:
|
||||
// CHECK: pair 0 - 1
|
||||
// CHECK-NEXT: %3 = ref_element_addr %2 : $X, #X.s
|
||||
// CHECK-NEXT: %5 = ref_element_addr %2 : $X, #X.s
|
||||
// CHECK-NEXT: may alias
|
||||
// CHECK: End function test_anyobject_cast
|
||||
sil @test_anyobject_cast : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%0 = alloc_ref $X
|
||||
%1 = init_existential_ref %0 : $X : $X, $AnyObject
|
||||
%2 = unchecked_ref_cast %1 : $AnyObject to $X
|
||||
%3 = ref_element_addr %2 : $X, #X.s
|
||||
fix_lifetime %3 : $*Str
|
||||
%5 = ref_element_addr %2 : $X, #X.s
|
||||
fix_lifetime %5 : $*Str
|
||||
%r = tuple ()
|
||||
return %r : $()
|
||||
}
|
||||
|
||||
|
||||
@@ -1445,3 +1445,15 @@ bb0(%0 : $F):
|
||||
return %r : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: Escape information for test_unchecked_ref_cast:
|
||||
// CHECK: return[]: %0 = alloc_ref $Derived
|
||||
// CHECK: - : %1 = alloc_ref $Derived
|
||||
// CHECK: End function test_unchecked_ref_cast
|
||||
sil @test_unchecked_ref_cast : $@convention(thin) () -> @owned X {
|
||||
bb0:
|
||||
%0 = alloc_ref $Derived
|
||||
%1 = alloc_ref $Derived
|
||||
%2 = unchecked_ref_cast %0 : $Derived to $X
|
||||
%3 = unchecked_ref_cast %1 : $Derived to $X
|
||||
return %2 : $X
|
||||
}
|
||||
|
||||
@@ -123,6 +123,11 @@ final class NewHalfOpenRangeGenerator : NewRangeGenerator1 {
|
||||
override init(start: Int32, end: Int32)
|
||||
}
|
||||
|
||||
class COpt {
|
||||
final var i: Optional<Int32>
|
||||
init()
|
||||
}
|
||||
|
||||
sil_global @total : $Int32
|
||||
|
||||
sil @use : $@convention(thin) (Builtin.Int32) -> ()
|
||||
@@ -1315,3 +1320,24 @@ bb0(%0 : $Int):
|
||||
dealloc_stack %1 : $*Int
|
||||
return %53 : $Int
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @test_anyobject_cast :
|
||||
// CHECK: store
|
||||
// CHECK: [[L:%[0-9]+]] = load
|
||||
// CHECK: return [[L]]
|
||||
// CHECK-LABEL: } // end sil function 'test_anyobject_cast'
|
||||
sil [ossa] @test_anyobject_cast : $@convention(thin) (@guaranteed COpt, Int32) -> Optional<Int32> {
|
||||
bb0(%0 : @guaranteed $COpt, %1 : $Int32):
|
||||
%2 = init_existential_ref %0 : $COpt : $COpt, $AnyObject
|
||||
%3 = unchecked_ref_cast %2 : $AnyObject to $COpt
|
||||
%4 = ref_element_addr %3 : $COpt, #COpt.i
|
||||
%5 = load [trivial] %4 : $*Optional<Int32>
|
||||
%6 = ref_element_addr %3 : $COpt, #COpt.i
|
||||
%7 = init_enum_data_addr %6 : $*Optional<Int32>, #Optional.some!enumelt
|
||||
store %1 to [trivial] %7 : $*Int32
|
||||
%9 = ref_element_addr %3 : $COpt, #COpt.i
|
||||
%10 = load [trivial] %9 : $*Optional<Int32>
|
||||
return %10 : $Optional<Int32>
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user