Skip to content

Move-only check the value projected from addressors. #70475

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
merged 1 commit into from
Dec 15, 2023
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
13 changes: 0 additions & 13 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3716,19 +3716,6 @@ class ArgEmitter {
Args.push_back(ManagedValue());
}

void emitExpandedConsumed(Expr *arg, AbstractionPattern origParamType) {
CanType substArgType = arg->getType()->getCanonicalType();
auto count = getFlattenedValueCount(origParamType);
auto claimedParams = claimNextParameters(count);

SILType loweredSubstArgType = SGF.getLoweredType(substArgType);
SILType loweredSubstParamType =
SGF.getLoweredType(origParamType, substArgType);

return emitConsumed(arg, loweredSubstArgType, loweredSubstParamType,
origParamType, claimedParams);
}

void
emitDirect(ArgumentSource &&arg, SILType loweredSubstArgType,
AbstractionPattern origParamType, SILParameterInfo param,
Expand Down
33 changes: 32 additions & 1 deletion lib/SILGen/SILGenLValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2009,6 +2009,25 @@ namespace {
class AddressorComponent
: public AccessorBasedComponent<PhysicalPathComponent> {
SILType SubstFieldType;

static SGFAccessKind getAccessKindForAddressor(SGFAccessKind accessKind) {
// Addressors cannot be consumed through.
switch (accessKind) {
case SGFAccessKind::IgnoredRead:
case SGFAccessKind::BorrowedAddressRead:
case SGFAccessKind::BorrowedObjectRead:
case SGFAccessKind::OwnedAddressRead:
case SGFAccessKind::OwnedObjectRead:
case SGFAccessKind::Write:
case SGFAccessKind::ReadWrite:
return accessKind;

case SGFAccessKind::OwnedAddressConsume:
case SGFAccessKind::OwnedObjectConsume:
return SGFAccessKind::ReadWrite;
}
llvm_unreachable("uncovered switch");
}
public:
AddressorComponent(AbstractStorageDecl *decl, SILDeclRef accessor,
bool isSuper,
Expand Down Expand Up @@ -2048,9 +2067,21 @@ namespace {

// Enter an unsafe access scope for the access.
addr =
enterAccessScope(SGF, loc, base, addr, getTypeData(), getAccessKind(),
enterAccessScope(SGF, loc, base, addr, getTypeData(),
getAccessKindForAddressor(getAccessKind()),
SILAccessEnforcement::Unsafe, ActorIso);

// Validate the use of the access if it's noncopyable.
if (addr.getType().isMoveOnly()) {
MarkUnresolvedNonCopyableValueInst::CheckKind kind
= getAccessorDecl()->getAccessorKind() == AccessorKind::MutableAddress
? MarkUnresolvedNonCopyableValueInst::CheckKind::ConsumableAndAssignable
: MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign;
auto checkedAddr = SGF.B.createMarkUnresolvedNonCopyableValueInst(
loc, addr.getValue(), kind);
addr = std::move(addr).transform(checkedAddr);
}

return addr;
}

Expand Down
19 changes: 14 additions & 5 deletions lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,12 @@ static bool isInOutDefThatNeedsEndOfFunctionLiveness(
MarkUnresolvedNonCopyableValueInst *markedAddr) {
SILValue operand = markedAddr->getOperand();

// Check for inout types of arguments that are marked with consumable and
// assignable.
if (markedAddr->getCheckKind() ==
MarkUnresolvedNonCopyableValueInst::CheckKind::ConsumableAndAssignable) {
// TODO: This should really be a property of the marker instruction.

// Check for inout types of arguments that are marked with consumable and
// assignable.
if (auto *fArg = dyn_cast<SILFunctionArgument>(operand)) {
switch (fArg->getArgumentConvention()) {
case SILArgumentConvention::Indirect_In:
Expand All @@ -402,9 +404,14 @@ static bool isInOutDefThatNeedsEndOfFunctionLiveness(
return true;
}
}
// Check for yields from a modify coroutine.
if (auto bai = dyn_cast_or_null<BeginApplyInst>(operand->getDefiningInstruction())) {
return true;
}
// Check for modify accesses.
if (auto access = dyn_cast<BeginAccessInst>(operand)) {
return access->getAccessKind() == SILAccessKind::Modify;
}
}

return false;
Expand Down Expand Up @@ -2181,8 +2188,10 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
if (moveChecker.canonicalizer.foundAnyConsumingUses()) {
LLVM_DEBUG(llvm::dbgs()
<< "Found mark must check [nocopy] error: " << *user);
auto *fArg = dyn_cast<SILFunctionArgument>(
stripAccessMarkers(markedValue->getOperand()));
auto operand = stripAccessMarkers(markedValue->getOperand());
auto *fArg = dyn_cast<SILFunctionArgument>(operand);
auto *ptrToAddr = dyn_cast<PointerToAddressInst>(operand);

// If we have a closure captured that we specialized, we should have a
// no consume or assign and should emit a normal guaranteed diagnostic.
if (fArg && fArg->isClosureCapture() &&
Expand All @@ -2198,7 +2207,7 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
// If we have a function argument that is no_consume_or_assign and we do
// not have any partial apply uses, then we know that we have a use of
// an address only borrowed parameter that we need to
if (fArg &&
if ((fArg || ptrToAddr) &&
checkKind == MarkUnresolvedNonCopyableValueInst::CheckKind::
NoConsumeOrAssign &&
!moveChecker.canonicalizer.hasPartialApplyConsumingUse()) {
Expand Down
123 changes: 123 additions & 0 deletions test/SILOptimizer/moveonly_addressors.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// RUN: %target-swift-frontend -enable-experimental-feature NonescapableTypes -enable-experimental-feature BuiltinModule -enable-experimental-feature NoncopyableGenerics -parse-stdlib -module-name Swift -DADDRESS_ONLY -emit-sil -verify %s
// RUN: %target-swift-frontend -enable-experimental-feature NonescapableTypes -enable-experimental-feature BuiltinModule -enable-experimental-feature NoncopyableGenerics -parse-stdlib -module-name Swift -DLOADABLE -emit-sil -verify %s
// RUN: %target-swift-frontend -enable-experimental-feature NonescapableTypes -enable-experimental-feature BuiltinModule -enable-experimental-feature NoncopyableGenerics -parse-stdlib -module-name Swift -DTRIVIAL -emit-sil -verify %s
// RUN: %target-swift-frontend -enable-experimental-feature NonescapableTypes -enable-experimental-feature BuiltinModule -enable-experimental-feature NoncopyableGenerics -parse-stdlib -module-name Swift -DEMPTY -emit-sil -verify %s

// TODO: Use the real stdlib types once `UnsafePointer` supports noncopyable
// types.

import Builtin

@_marker public protocol Copyable: ~Escapable {}
@_marker public protocol Escapable: ~Copyable {}
@frozen public struct UnsafePointer<T: ~Copyable>: Copyable {
var value: Builtin.RawPointer
}

@frozen public struct UnsafeMutablePointer<T: ~Copyable>: Copyable {
var value: Builtin.RawPointer
}

@frozen public struct Int { var value: Builtin.Word }

@_silgen_name("makeUpAPointer")
func makeUpAPointer<T: ~Copyable>() -> UnsafePointer<T>
@_silgen_name("makeUpAMutablePointer")
func makeUpAPointer<T: ~Copyable>() -> UnsafeMutablePointer<T>
@_silgen_name("makeUpAnInt")
func makeUpAnInt() -> Int

class X {}

struct NC: ~Copyable {
#if EMPTY
#elseif TRIVIAL
var x: Int = makeUpAnInt()
#elseif LOADABLE
var x: X = X()
#elseif ADDRESS_ONLY
var x: Any = X()
#else
#error("pick a mode")
#endif
deinit {}
}

struct S {
var data: NC {
unsafeAddress { return makeUpAPointer() }
}

var mutableData: NC {
unsafeAddress { return makeUpAPointer() }
unsafeMutableAddress { return makeUpAPointer() }
}
}

struct SNC: ~Copyable {
var data: NC {
unsafeAddress { return makeUpAPointer() }
}

var mutableData: NC {
unsafeAddress { return makeUpAPointer() }
unsafeMutableAddress { return makeUpAPointer() }
}
}

class C {
final var data: NC {
unsafeAddress { return makeUpAPointer() }
}

final var mutableData: NC {
unsafeAddress { return makeUpAPointer() }
unsafeMutableAddress { return makeUpAPointer() }
}
}

func borrow(_ nc: borrowing NC) {}
func mod(_ nc: inout NC) {}
func take(_ nc: consuming NC) {}

// TODO: Resolve thing being consumed more precisely than 'unknown'.
// TODO: Use more specific diagnostic than "reinitialization of inout parameter"

func test(c: C) {
borrow(c.data)
take(c.data) // expected-error{{'unknown' is borrowed and cannot be consumed}} expected-note{{consumed here}}

borrow(c.mutableData)
mod(&c.mutableData)
take(c.mutableData) // expected-error{{missing reinitialization of inout parameter 'unknown' after consume}} expected-note{{consumed here}}
}
func test(s: S) {
borrow(s.data)
take(s.data) // expected-error{{'unknown' is borrowed and cannot be consumed}} expected-note{{consumed here}}

borrow(s.mutableData)
take(s.mutableData) // expected-error{{'unknown' is borrowed and cannot be consumed}} expected-note{{consumed here}}
}
func test(mut_s s: inout S) {
borrow(s.data)
take(s.data) // expected-error{{'unknown' is borrowed and cannot be consumed}} expected-note{{consumed here}}

borrow(s.mutableData)
mod(&s.mutableData)
take(s.mutableData) // expected-error{{missing reinitialization of inout parameter 'unknown' after consume}} expected-note{{consumed here}}
}
func test(snc: borrowing SNC) {
borrow(snc.data)
take(snc.data) // expected-error{{'unknown' is borrowed and cannot be consumed}} expected-note{{consumed here}}

borrow(snc.mutableData)
take(snc.mutableData) // expected-error{{'unknown' is borrowed and cannot be consumed}} expected-note{{consumed here}}
}
func test(mut_snc snc: inout SNC) {
borrow(snc.data)
take(snc.data) // expected-error{{'unknown' is borrowed and cannot be consumed}} expected-note{{consumed here}}

borrow(snc.mutableData)
mod(&snc.mutableData)
take(snc.mutableData) // expected-error{{missing reinitialization of inout parameter 'unknown' after consume}} expected-note{{consumed here}}
}