Skip to content

[silgen] When forwarding a tuple value, make sure that it is at +1. #13891

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions lib/SILGen/RValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ class ImplodeLoadableTupleValue
case ImplodeKind::Unmanaged:
return v.getUnmanagedValue();
case ImplodeKind::Forward:
return v.forward(SGF);
return v.ensurePlusOne(SGF, l).forward(SGF);
case ImplodeKind::Copy:
return v.copyUnmanaged(SGF, l).forward(SGF);
}
Expand Down Expand Up @@ -242,7 +242,10 @@ class ImplodeAddressOnlyTuple
llvm_unreachable("address-only types always managed!");

case ImplodeKind::Forward:
v.forwardInto(SGF, l, address);
// If a value is forwarded into, we require the value to be at +1. If the
// the value is already at +1, we just forward. Otherwise, we perform the
// copy.
v.ensurePlusOne(SGF, l).forwardInto(SGF, l, address);
break;

case ImplodeKind::Copy:
Expand Down Expand Up @@ -510,10 +513,9 @@ void RValue::addElement(SILGenFunction &SGF, ManagedValue element,

SILValue RValue::forwardAsSingleValue(SILGenFunction &SGF, SILLocation l) && {
assert(isComplete() && "rvalue is not complete");
assert(isPlusOne(SGF) && "Can not forward borrowed RValues");
// *NOTE* Inside implodeTupleValues, we copy our values if they are not at +1.
SILValue result
= implodeTupleValues<ImplodeKind::Forward>(values, SGF, type, l);

makeUsed();
return result;
}
Expand All @@ -522,8 +524,9 @@ SILValue RValue::forwardAsSingleStorageValue(SILGenFunction &SGF,
SILType storageType,
SILLocation l) && {
assert(isComplete() && "rvalue is not complete");
assert(isPlusOne(SGF) && "Can not forward borrowed RValues");
SILValue result = std::move(*this).forwardAsSingleValue(SGF, l);
// Conversions must always be done at +1.
SILValue result =
std::move(*this).ensurePlusOne(SGF, l).forwardAsSingleValue(SGF, l);
return SGF.emitConversionFromSemanticValue(l, result, storageType);
}

Expand Down
90 changes: 90 additions & 0 deletions test/SILGen/guaranteed_normal_args.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,33 @@ precedencegroup AssignmentPrecedence {
assignment: true
}

public protocol ExpressibleByNilLiteral {
init(nilLiteral: ())
}

protocol IteratorProtocol {
associatedtype Element
mutating func next() -> Element?
}

protocol Sequence {
associatedtype Element
associatedtype Iterator : IteratorProtocol where Iterator.Element == Element

func makeIterator() -> Iterator
}

enum Optional<T> {
case none
case some(T)
}

extension Optional : ExpressibleByNilLiteral {
public init(nilLiteral: ()) {
self = .none
}
}

func _diagnoseUnexpectedNilOptional(_filenameStart: Builtin.RawPointer,
_filenameLength: Builtin.Word,
_filenameIsASCII: Builtin.Int1,
Expand All @@ -42,6 +64,41 @@ protocol Protocol {
static func useInput(_ input: Builtin.Int32, into processInput: (AssocType) -> ())
}

struct FakeArray<Element> {
// Just to make this type non-trivial
var k: Klass

// We are only interested in this being called. We are not interested in its
// implementation.
mutating func append(_ t: Element) {}
}

struct FakeDictionary<Key, Value> {
}

struct FakeDictionaryIterator<Key, Value> {
var dictionary: FakeDictionary<Key, Value>?

init(_ newDictionary: FakeDictionary<Key, Value>) {
dictionary = newDictionary
}
}

extension FakeDictionaryIterator : IteratorProtocol {
public typealias Element = (Key, Value)
public mutating func next() -> Element? {
return .none
}
}

extension FakeDictionary : Sequence {
public typealias Element = (Key, Value)
public typealias Iterator = FakeDictionaryIterator<Key, Value>
public func makeIterator() -> FakeDictionaryIterator<Key, Value> {
return FakeDictionaryIterator(self)
}
}

///////////
// Tests //
///////////
Expand Down Expand Up @@ -108,3 +165,36 @@ struct ReabstractionThunkTest : Protocol {
}
}

// Make sure that we provide a cleanup to x properly before we pass it to
// result.
extension FakeDictionary {
// CHECK-LABEL: sil hidden @$Ss14FakeDictionaryV20makeSureToCopyTuplesyyF : $@convention(method) <Key, Value> (FakeDictionary<Key, Value>) -> () {
// CHECK: [[X:%.*]] = alloc_stack $(Key, Value), let, name "x"
// CHECK: [[INDUCTION_VAR:%.*]] = unchecked_take_enum_data_addr {{%.*}} : $*Optional<(Key, Value)>, #Optional.some!enumelt.1
// CHECK: [[INDUCTION_VAR_0:%.*]] = tuple_element_addr [[INDUCTION_VAR]] : $*(Key, Value), 0
// CHECK: [[INDUCTION_VAR_1:%.*]] = tuple_element_addr [[INDUCTION_VAR]] : $*(Key, Value), 1
// CHECK: [[X_0:%.*]] = tuple_element_addr [[X]] : $*(Key, Value), 0
// CHECK: [[X_1:%.*]] = tuple_element_addr [[X]] : $*(Key, Value), 1
// CHECK: copy_addr [take] [[INDUCTION_VAR_0]] to [initialization] [[X_0]]
// CHECK: copy_addr [take] [[INDUCTION_VAR_1]] to [initialization] [[X_1]]
// CHECK: [[X_0:%.*]] = tuple_element_addr [[X]] : $*(Key, Value), 0
// CHECK: [[X_1:%.*]] = tuple_element_addr [[X]] : $*(Key, Value), 1
// CHECK: [[TMP_X:%.*]] = alloc_stack $(Key, Value)
// CHECK: [[TMP_X_0:%.*]] = tuple_element_addr [[TMP_X]] : $*(Key, Value), 0
// CHECK: [[TMP_0:%.*]] = alloc_stack $Key
// CHECK: copy_addr [[X_0]] to [initialization] [[TMP_0]]
// CHECK: copy_addr [take] [[TMP_0]] to [initialization] [[TMP_X_0]]
// CHECK: [[TMP_X_1:%.*]] = tuple_element_addr [[TMP_X]] : $*(Key, Value), 1
// CHECK: [[TMP_1:%.*]] = alloc_stack $Value
// CHECK: copy_addr [[X_1]] to [initialization] [[TMP_1]]
// CHECK: copy_addr [take] [[TMP_1]] to [initialization] [[TMP_X_1]]
// CHECK: [[FUNC:%.*]] = function_ref @$Ss9FakeArrayV6appendyyxF : $@convention(method) <τ_0_0> (@in_guaranteed τ_0_0, @inout FakeArray<τ_0_0>) -> ()
// CHECK: apply [[FUNC]]<(Key, Value)>([[TMP_X]],
// CHECK: } // end sil function '$Ss14FakeDictionaryV20makeSureToCopyTuplesyyF'
func makeSureToCopyTuples() {
var result = FakeArray<Element>(k: Klass())
for x in self {
result.append(x)
}
}
}