[sil-devirtualizer] Properly handle devirtualization of functions with tuple return types, e.g. materializeForSet.

rdar://21603216

Swift SVN r29828
This commit is contained in:
Roman Levenstein
2015-07-01 00:50:40 +00:00
parent 9da16e592e
commit ec97f37a81
2 changed files with 109 additions and 0 deletions

View File

@@ -311,6 +311,50 @@ static SILValue conditionallyCastAddr(SILBuilderWithScope<16> &B,
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.
///
/// \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
// 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
// or the other way around
bool UnwrapOptionalResult = false;

View 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())