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);
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
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