mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[sil-devirtualizer] Properly handle devirtualization of functions with tuple return types, e.g. materializeForSet.
rdar://21603216 Swift SVN r29828
This commit is contained in:
@@ -311,6 +311,50 @@ static SILValue conditionallyCastAddr(SILBuilderWithScope<16> &B,
|
|||||||
return B.createUncheckedRefCast(Loc, Arg, ParamTy);
|
return B.createUncheckedRefCast(Loc, Arg, ParamTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert instructions to cast the tuple return type into appropiate
|
||||||
|
/// tuple type expected by the original apply_inst.
|
||||||
|
static SILInstruction *castTupleReturnType(ApplyInst *AI, ApplyInst *NewAI,
|
||||||
|
CanTypeWrapper<TupleType> ResultTupleTy, SILFunction *F, SILBuilder& B) {
|
||||||
|
auto AITupleTy = cast<TupleType>(AI->getType().getSwiftRValueType());
|
||||||
|
SmallVector<SILValue, 4> TupleElements;
|
||||||
|
auto TupleElementTypes = ResultTupleTy.getElementTypes();
|
||||||
|
unsigned NumElements = ResultTupleTy->getElements().size();
|
||||||
|
for (unsigned i = 0; i < NumElements; ++i) {
|
||||||
|
auto EltTy = TupleElementTypes[i];
|
||||||
|
auto ExtractedElt = B.createTupleExtract(AI->getLoc(), NewAI, i);
|
||||||
|
OptionalTypeKind OTK;
|
||||||
|
auto OptionalEltTy =
|
||||||
|
EltTy.getCanonicalTypeOrNull()->getAnyOptionalObjectType(OTK);
|
||||||
|
if (!OptionalEltTy
|
||||||
|
|| !isa<FunctionType>(OptionalEltTy.getCanonicalTypeOrNull())) {
|
||||||
|
// No need to convert this parameter
|
||||||
|
TupleElements.push_back(ExtractedElt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dereference the optional value
|
||||||
|
auto *SomeDecl = B.getASTContext().getOptionalSomeDecl(OTK);
|
||||||
|
auto FuncPtr = B.createUncheckedEnumData(AI->getLoc(), ExtractedElt,
|
||||||
|
SomeDecl);
|
||||||
|
|
||||||
|
auto AIOptionalEltTy =
|
||||||
|
AITupleTy.getElementType(i).getCanonicalTypeOrNull()->getAnyOptionalObjectType();
|
||||||
|
auto SILAIOptionalEltTy = AI->getModule().Types.getLoweredType(
|
||||||
|
AIOptionalEltTy);
|
||||||
|
auto ConvertedFuncPtr = B.createConvertFunction(AI->getLoc(), FuncPtr,
|
||||||
|
SILAIOptionalEltTy);
|
||||||
|
|
||||||
|
TupleElements.push_back(
|
||||||
|
B.createOptionalSome(AI->getLoc(), ConvertedFuncPtr, OTK,
|
||||||
|
SILType::getPrimitiveObjectType(AITupleTy.getElementType(i))));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now create a new tuple
|
||||||
|
DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n");
|
||||||
|
NumClassDevirt++;
|
||||||
|
return B.createTuple(AI->getLoc(), AI->getType(), TupleElements);
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Devirtualize an apply of a class method.
|
/// \brief Devirtualize an apply of a class method.
|
||||||
///
|
///
|
||||||
/// \p AI is the apply to devirtualize.
|
/// \p AI is the apply to devirtualize.
|
||||||
@@ -380,6 +424,20 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI,
|
|||||||
// If our return type differs from AI's return type, then we know that we have
|
// If our return type differs from AI's return type, then we know that we have
|
||||||
// a covariant return type. Cast it before we RAUW. This can not happen
|
// a covariant return type. Cast it before we RAUW. This can not happen
|
||||||
|
|
||||||
|
// Accessors could return a tuple where one of the elements is of a function
|
||||||
|
// type, which may refer to a subclass instead of a superclass in its
|
||||||
|
// signature.
|
||||||
|
// These function types are ABI-compatible between the subclass and a
|
||||||
|
// superclass. To make the SIL type system happy, insert a conversion between
|
||||||
|
// the function types and reconstruct the tuple.
|
||||||
|
if (auto *FD = dyn_cast<FuncDecl>(CMI->getMember().getDecl())) {
|
||||||
|
if (FD->isAccessor()) {
|
||||||
|
if (auto ResultTupleTy = dyn_cast<TupleType>(ReturnType.getSwiftRValueType()))
|
||||||
|
return castTupleReturnType(AI, NewAI, ResultTupleTy, F, B);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check if the return type is an optional of the apply_inst type
|
// Check if the return type is an optional of the apply_inst type
|
||||||
// or the other way around
|
// or the other way around
|
||||||
bool UnwrapOptionalResult = false;
|
bool UnwrapOptionalResult = false;
|
||||||
|
|||||||
51
test/SILPasses/devirt_materializeForSet.swift
Normal file
51
test/SILPasses/devirt_materializeForSet.swift
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// RUN: %target-swift-frontend -O -emit-sil %s | FileCheck %s
|
||||||
|
|
||||||
|
// Check that compiler does not crash on the devirtualization of materializeForSet methods
|
||||||
|
// and produces a correct code.
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil [transparent] [thunk] @_TTWC24devirt_materializeForSet7BaseFooS_3FooS_FS1_m3barSS
|
||||||
|
// CHECK: checked_cast_br [exact] %{{.*}} : $BaseFoo to $ChildFoo
|
||||||
|
// CHECK: convert_function %{{.*}} : $@convention(thin) (Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @inout ChildFoo, @thick ChildFoo.Type) -> () to $@convention(thin) (Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @inout BaseFoo, @thick BaseFoo.Type) -> ()
|
||||||
|
// CHECK: enum $Optional<@convention(thin) (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer, inout BaseFoo, @thick BaseFoo.Type) -> ()>, #Optional.Some!enumelt.1, %{{.*}} : $@convention(thin) (Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @inout BaseFoo, @thick BaseFoo.Type) -> ()
|
||||||
|
// CHECK: tuple (%{{.*}} : $Builtin.RawPointer, %{{.*}} : $Optional<@convention(thin) (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer, inout BaseFoo, @thick BaseFoo.Type) -> ()>)
|
||||||
|
|
||||||
|
public protocol Foo {
|
||||||
|
var bar: String { get set }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BaseFoo: Foo {
|
||||||
|
public var bar: String = "hello"
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChildFoo: BaseFoo {
|
||||||
|
private var _bar: String = "world"
|
||||||
|
|
||||||
|
override public var bar: String {
|
||||||
|
get {
|
||||||
|
return _bar
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_bar = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@inline(never)
|
||||||
|
public func test1(bf: BaseFoo) {
|
||||||
|
bf.bar = "test1"
|
||||||
|
print(bf.bar)
|
||||||
|
}
|
||||||
|
|
||||||
|
@inline(never)
|
||||||
|
public func test2(var f: Foo) {
|
||||||
|
f.bar = "test2"
|
||||||
|
print(f.bar)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test1(BaseFoo())
|
||||||
|
//test1(ChildFoo())
|
||||||
|
|
||||||
|
//test2(BaseFoo())
|
||||||
|
//test2(ChildFoo())
|
||||||
Reference in New Issue
Block a user