Skip to content

Commit a4e65eb

Browse files
committed
SILCombine: fix propagation of existential self into witness method call
In case of a mutating method call we replaced the self argument with the source of a copy_addr. This let the call modify the wrong self value. rdar://problem/34753633
1 parent 8ed5ab3 commit a4e65eb

File tree

2 files changed

+65
-7
lines changed

2 files changed

+65
-7
lines changed

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -567,8 +567,11 @@ SILCombiner::optimizeConcatenationOfStringLiterals(ApplyInst *AI) {
567567
/// initialized. This is either a init_existential_addr or the source of a
568568
/// copy_addr. Returns a null value if the address does not dominate the
569569
/// alloc_stack user \p ASIUser.
570+
/// If the value is copied from another stack location, \p isCopied is set to
571+
/// true.
570572
static SILValue getAddressOfStackInit(AllocStackInst *ASI,
571-
SILInstruction *ASIUser) {
573+
SILInstruction *ASIUser,
574+
bool &isCopied) {
572575
SILInstruction *SingleWrite = nullptr;
573576
// Check that this alloc_stack is initialized only once.
574577
for (auto Use : ASI->getUses()) {
@@ -588,6 +591,7 @@ static SILValue getAddressOfStackInit(AllocStackInst *ASI,
588591
if (SingleWrite)
589592
return SILValue();
590593
SingleWrite = CAI;
594+
isCopied = true;
591595
}
592596
continue;
593597
}
@@ -621,23 +625,29 @@ static SILValue getAddressOfStackInit(AllocStackInst *ASI,
621625
if (auto *CAI = dyn_cast<CopyAddrInst>(SingleWrite)) {
622626
// Try to derive the type from the copy_addr that was used to
623627
// initialize the alloc_stack.
628+
assert(isCopied && "isCopied not set for a copy_addr");
624629
SILValue CAISrc = CAI->getSrc();
625630
if (auto *ASI = dyn_cast<AllocStackInst>(CAISrc))
626-
return getAddressOfStackInit(ASI, CAI);
631+
return getAddressOfStackInit(ASI, CAI, isCopied);
627632
return CAISrc;
628633
}
629634
return SingleWrite;
630635
}
631636

632637
/// Find the init_existential, which could be used to determine a concrete
633638
/// type of the \p Self.
639+
/// If the value is copied from another stack location, \p isCopied is set to
640+
/// true.
634641
static SILInstruction *findInitExistential(FullApplySite AI, SILValue Self,
635642
ArchetypeType *&OpenedArchetype,
636-
SILValue &OpenedArchetypeDef) {
643+
SILValue &OpenedArchetypeDef,
644+
bool &isCopied) {
645+
isCopied = false;
637646
if (auto *Instance = dyn_cast<AllocStackInst>(Self)) {
638647
// In case the Self operand is an alloc_stack where a copy_addr copies the
639648
// result of an open_existential_addr to this stack location.
640-
if (SILValue Src = getAddressOfStackInit(Instance, AI.getInstruction()))
649+
if (SILValue Src = getAddressOfStackInit(Instance, AI.getInstruction(),
650+
isCopied))
641651
Self = Src;
642652
}
643653

@@ -647,7 +657,7 @@ static SILInstruction *findInitExistential(FullApplySite AI, SILValue Self,
647657
if (!ASI)
648658
return nullptr;
649659

650-
SILValue StackWrite = getAddressOfStackInit(ASI, Open);
660+
SILValue StackWrite = getAddressOfStackInit(ASI, Open, isCopied);
651661
if (!StackWrite)
652662
return nullptr;
653663

@@ -853,8 +863,10 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI,
853863
// determine a concrete type of the self.
854864
ArchetypeType *OpenedArchetype = nullptr;
855865
SILValue OpenedArchetypeDef;
866+
bool isCopied = false;
856867
SILInstruction *InitExistential =
857-
findInitExistential(AI, Self, OpenedArchetype, OpenedArchetypeDef);
868+
findInitExistential(AI, Self, OpenedArchetype, OpenedArchetypeDef,
869+
isCopied);
858870
if (!InitExistential)
859871
return nullptr;
860872

@@ -888,6 +900,18 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI,
888900
// Propagate the concrete type into the callee-operand if required.
889901
Propagate(ConcreteType, Conformance);
890902

903+
if (isCopied) {
904+
// If the witness method is mutating self, we cannot replace self with
905+
// the source of a copy. Otherwise the call would modify another value than
906+
// the original self.
907+
switch (AI.getArgumentConvention(AI.getNumArguments() - 1)) {
908+
case SILArgumentConvention::ConventionType::Indirect_Inout:
909+
case SILArgumentConvention::ConventionType::Indirect_InoutAliasable:
910+
return nullptr;
911+
default:
912+
break;
913+
}
914+
}
891915
// Create a new apply instruction that uses the concrete type instead
892916
// of the existential type.
893917
auto *NewAI = createApplyWithConcreteType(AI, NewSelf, Self, ConcreteType,

test/SILOptimizer/sil_combine_apply.sil

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,4 +308,38 @@ bb0(%0 : $*SwiftP):
308308
%4 = apply %3() : $@callee_owned () -> ()
309309
%9999 = tuple()
310310
return %9999 : $()
311-
}
311+
}
312+
313+
protocol MutatingProto {
314+
mutating func mutatingMethod()
315+
}
316+
317+
struct MStruct : MutatingProto {
318+
319+
var somevar: Builtin.Int32
320+
321+
mutating func mutatingMethod()
322+
}
323+
324+
// CHECK-LABEL: sil @dont_replace_copied_self_in_mutating_method_call
325+
sil @dont_replace_copied_self_in_mutating_method_call : $@convention(thin) (MStruct) -> (@out MutatingProto) {
326+
bb0(%0 : $*MutatingProto, %1 : $MStruct):
327+
%2 = alloc_stack $MutatingProto
328+
%4 = init_existential_addr %2 : $*MutatingProto, $MStruct
329+
store %1 to %4 : $*MStruct
330+
%9 = alloc_stack $MutatingProto
331+
copy_addr %2 to [initialization] %9 : $*MutatingProto
332+
// CHECK: [[E:%[0-9]+]] = open_existential_addr
333+
%11 = open_existential_addr mutable_access %9 : $*MutatingProto to $*@opened("FC5F3CFA-A7A4-11E7-911F-685B35C48C83") MutatingProto
334+
// CHECK: [[M:%[0-9]+]] = witness_method $MStruct,
335+
%12 = witness_method $@opened("FC5F3CFA-A7A4-11E7-911F-685B35C48C83") MutatingProto, #MutatingProto.mutatingMethod!1 : <Self where Self : MutatingProto> (inout Self) -> () -> (), %11 : $*@opened("FC5F3CFA-A7A4-11E7-911F-685B35C48C83") MutatingProto : $@convention(witness_method) <τ_0_0 where τ_0_0 : MutatingProto> (@inout τ_0_0) -> ()
336+
// CHECK: apply [[M]]<@opened("{{.*}}") MutatingProto>([[E]]) :
337+
%13 = apply %12<@opened("FC5F3CFA-A7A4-11E7-911F-685B35C48C83") MutatingProto>(%11) : $@convention(witness_method) <τ_0_0 where τ_0_0 : MutatingProto> (@inout τ_0_0) -> ()
338+
copy_addr [take] %9 to [initialization] %0 : $*MutatingProto
339+
dealloc_stack %9 : $*MutatingProto
340+
destroy_addr %2 : $*MutatingProto
341+
dealloc_stack %2 : $*MutatingProto
342+
%27 = tuple ()
343+
return %27 : $()
344+
}
345+

0 commit comments

Comments
 (0)