[SIL] Hollow out Builtin.copy, deprecate _copy.

The copy operator has been implemented and doesn't use it.  Remove
`Builtin.copy` and `_copy` as much as currently possible.

Source compatibility requires that `_copy` remain in the stdlib.  It is
deprecated here and just uses the copy operator.

Handling old swiftinterfaces requires that `Builtin.copy` be defined.
Redefine it here as a passthrough--SILGen machinery will produce the
necessary copy_addr.

rdar://127502242
This commit is contained in:
Nate Chandler
2024-05-03 15:53:36 -07:00
parent 6fdaea514f
commit 06921cfe84
20 changed files with 98 additions and 260 deletions

View File

@@ -165,12 +165,6 @@ extension AddressUseVisitor {
case let builtin as BuiltinInst:
switch builtin.id {
case .Copy where builtin.operands[1] == operand: // source
return loadedAddressUse(of: operand, into: builtin.operands[0])
case .Copy where builtin.operands[0] == operand: // dest
return leafAddressUse(of: operand)
// Builtins that cannot load a nontrivial value.
case .TSanInoutAccess, .ResumeThrowingContinuationReturning,
.ResumeNonThrowingContinuationReturning, .GenericAdd,

View File

@@ -909,12 +909,6 @@ extension LifetimeDependenceDefUseWalker {
case let tai as TupleAddrConstructorInst:
return visitStoredUses(of: operand, into: tai.destinationOperand.value)
case let bi as BuiltinInst where bi.id == .Copy:
// This must be a non-address-lowered form of Builtin.Copy that
// produces an owned value.
assert(bi.ownership == .owned)
return walkDownUses(of: bi, using: operand)
default:
return nonForwardingUse(of: operand)
}

View File

@@ -537,6 +537,14 @@ BUILTIN_SIL_OPERATION(HopToActor, "hopToActor", None)
/// Returns the number of items in a pack.
BUILTIN_SIL_OPERATION(PackLength, "packLength", Special)
/// TODO: Remove. Only exists to avoid a reverse condfail.
/// rdar://127516085 (Complete removal of Builtin.copy)
///
/// copy: <T>(T) -> T
///
/// Creates a copy of the source at the destination.
BUILTIN_SIL_OPERATION(Copy, "copy", Special)
// BUILTIN_RUNTIME_CALL - A call into a runtime function.
// These functions accept a single argument of any type.
#ifndef BUILTIN_RUNTIME_CALL
@@ -804,24 +812,6 @@ BUILTIN_MISC_OPERATION(CreateTaskGroupWithFlags,
BUILTIN_MISC_OPERATION(DestroyTaskGroup,
"destroyTaskGroup", "", Special)
/// A builtin that can only be called from a transparent generic function. Takes
/// two operands, the first operand the result address, the second operand the
/// input address. Transforms into
///
/// %input = load [take] %inputAddr
/// %result = explicit_copy_value %input
/// store %result to [init] %resultAddr
/// store %input to [init] %inputAddr
///
/// transparently inlined into a caller that has the generic of the callee
/// specialized into a loadable type. If the transparent inlining does not
/// specialize the type (due to being inlined into a non-generic context, the
/// SILVerifier will abort).
///
/// Illegal to call except for in Swift._copy in the stdlib. This is enforced by
/// the SILVerifier.
BUILTIN_MISC_OPERATION(Copy, "copy", "", Special)
/// Unchecked pointer alignment assertion. Allows the compiler to assume
/// alignment of the pointer to emit more efficient code.
///

View File

@@ -124,7 +124,6 @@ SEMANTICS_ATTR(FORCE_EMIT_OPT_REMARK_PREFIX, "optremark")
SEMANTICS_ATTR(OBJC_FORBID_ASSOCIATED_OBJECTS, "objc.forbidAssociatedObjects")
SEMANTICS_ATTR(LIFETIMEMANAGEMENT_MOVE, "lifetimemanagement.move")
SEMANTICS_ATTR(LIFETIMEMANAGEMENT_COPY, "lifetimemanagement.copy")
SEMANTICS_ATTR(NO_PERFORMANCE_ANALYSIS, "no_performance_analysis")

View File

@@ -251,7 +251,6 @@ TransitiveAddressWalker<Impl>::walk(SILValue projectedAddress) && {
case BuiltinValueKind::TSanInoutAccess:
case BuiltinValueKind::ResumeThrowingContinuationReturning:
case BuiltinValueKind::ResumeNonThrowingContinuationReturning:
case BuiltinValueKind::Copy:
case BuiltinValueKind::GenericAdd:
case BuiltinValueKind::GenericFAdd:
case BuiltinValueKind::GenericAnd:

View File

@@ -1437,16 +1437,6 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
return;
}
if (Builtin.ID == BuiltinValueKind::Copy) {
auto input = args.claimNext();
auto result = args.claimNext();
SILType addrTy = argTypes[0];
const TypeInfo &addrTI = IGF.getTypeInfo(addrTy);
Address inputAttr = addrTI.getAddressForPointer(input);
Address resultAttr = addrTI.getAddressForPointer(result);
addrTI.initializeWithCopy(IGF, resultAttr, inputAttr, addrTy, false);
return;
}
if (Builtin.ID == BuiltinValueKind::AssumeAlignment) {
// A no-op pointer cast that passes on its first value. Common occurrences of
// this builtin should already be removed with the alignment guarantee moved

View File

@@ -899,14 +899,6 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GetEnumTag)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, InjectEnumTag)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, DistributedActorAsAnyActor)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AddressOfRawLayout)
OperandOwnership OperandOwnershipBuiltinClassifier::visitCopy(BuiltinInst *bi,
StringRef) {
if (bi->getFunction()->getConventions().useLoweredAddresses()) {
return OperandOwnership::UnownedInstantaneousUse;
} else {
return OperandOwnership::DestroyingConsume;
}
}
BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, StartAsyncLet)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, EndAsyncLet)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, EndAsyncLetLifetime)

View File

@@ -629,7 +629,6 @@ CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroup)
CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroupWithFlags)
CONSTANT_OWNERSHIP_BUILTIN(None, DestroyTaskGroup)
CONSTANT_OWNERSHIP_BUILTIN(None, TaskRunInline)
CONSTANT_OWNERSHIP_BUILTIN(None, Copy)
CONSTANT_OWNERSHIP_BUILTIN(None, GetEnumTag)
CONSTANT_OWNERSHIP_BUILTIN(None, InjectEnumTag)
CONSTANT_OWNERSHIP_BUILTIN(Owned, DistributedActorAsAnyActor)

View File

@@ -2592,11 +2592,6 @@ static void visitBuiltinAddress(BuiltinInst *builtin,
visitor(&builtin->getAllOperands()[0]);
return;
// These effect both operands.
case BuiltinValueKind::Copy:
visitor(&builtin->getAllOperands()[1]);
return;
// These consume values out of their second operand.
case BuiltinValueKind::ResumeNonThrowingContinuationReturning:
case BuiltinValueKind::ResumeThrowingContinuationReturning:

View File

@@ -2343,22 +2343,6 @@ public:
return;
}
if (builtinKind == BuiltinValueKind::Copy) {
// We expect that this builtin will be specialized during transparent
// inlining into explicit_copy_value if we inline into a non-generic
// context. If the builtin still remains and is not in the specific copy
// semantic function (which is the only function marked with
// semantics::LIFETIMEMANAGEMENT_COPY), then we know that we did
// transparent inlining into a function that did not result in the Builtin
// being specialized out which is user error.
//
// NOTE: Once we have opaque values, this restriction will go away. This
// is just so we can call Builtin.copy outside of the stdlib.
auto semanticName = semantics::LIFETIMEMANAGEMENT_COPY;
require(BI->getFunction()->hasSemanticsAttr(semanticName),
"_copy used within a generic context");
}
if (builtinKind == BuiltinValueKind::CreateAsyncTask) {
requireType(BI->getType(), _object(_tuple(_nativeObject, _rawPointer)),
"result of createAsyncTask");

View File

@@ -2027,6 +2027,23 @@ static ManagedValue emitBuiltinAddressOfRawLayout(SILGenFunction &SGF,
return ManagedValue::forObjectRValueWithoutOwnership(bi);
}
/// TODO: Remove. Only exists to avoid a reverse condfail.
/// rdar://127516085 (Complete removal of Builtin.copy)
static ManagedValue emitBuiltinCopy(SILGenFunction &SGF, SILLocation loc,
SubstitutionMap subs,
ArrayRef<ManagedValue> args, SGFContext C) {
// Builtin.copy has no uses in current/future swiftinterfaces. It has a
// single use in old swiftinterfaces:
// func _copy<T>(_ t: T) -> T {
// Builtin.copy(t)
// }
// It is sufficient to have the builtin pass the value through. When the
// return is emitted, a copy will be made.
assert(args.size() == 1 && "not two arguments!?");
return ManagedValue::forOwnedAddressRValue(args[0].getValue(),
CleanupHandle::invalid());
}
std::optional<SpecializedEmitter>
SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) {
// Only consider standalone declarations in the Builtin module.

View File

@@ -3389,11 +3389,6 @@ protected:
bi->setOperand(use->getOperandNumber(), opAddr);
break;
}
case BuiltinValueKind::Copy: {
SILValue opAddr = addrMat.materializeAddress(use->get());
bi->setOperand(0, opAddr);
break;
}
case BuiltinValueKind::AddressOfBorrowOpaque:
visitAddressOfBorrowBuiltinInst(bi, /*stackProtected=*/true);
break;
@@ -4084,14 +4079,6 @@ protected:
void visitBuiltinInst(BuiltinInst *bi) {
switch (bi->getBuiltinKind().value_or(BuiltinValueKind::None)) {
case BuiltinValueKind::Copy: {
SILValue addr = addrMat.materializeAddress(bi);
builder.createBuiltin(
bi->getLoc(), bi->getName(),
SILType::getEmptyTupleType(bi->getType().getASTContext()),
bi->getSubstitutions(), {addr, bi->getOperand(0)});
break;
}
default:
bi->dump();
llvm::report_fatal_error("^^^ Unimplemented builtin opaque value def.");

View File

@@ -183,7 +183,6 @@ static bool isBarrier(SILInstruction *inst) {
case BuiltinValueKind::AssignCopyArrayFrontToBack:
case BuiltinValueKind::AssignCopyArrayBackToFront:
case BuiltinValueKind::AssignTakeArray:
case BuiltinValueKind::Copy:
case BuiltinValueKind::CancelAsyncTask:
case BuiltinValueKind::StartAsyncLet:
case BuiltinValueKind::CreateAsyncTask:

View File

@@ -320,7 +320,6 @@ protected:
void visitHopToExecutorInst(HopToExecutorInst *Inst);
void visitTerminator(SILBasicBlock *BB);
void visitBuiltinInst(BuiltinInst *BI);
/// This hook is called after either of the top-level visitors:
/// cloneReachableBlocks or cloneSILFunction.
@@ -820,51 +819,6 @@ static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
Context.Diags.diagnose(loc, diag, std::forward<U>(args)...);
}
void SILInlineCloner::visitBuiltinInst(BuiltinInst *Inst) {
if (IKind == InlineKind::MandatoryInline) {
if (auto kind = Inst->getBuiltinKind()) {
if (*kind == BuiltinValueKind::Copy) {
auto otherResultAddr = getOpValue(Inst->getOperand(0));
auto otherSrcAddr = getOpValue(Inst->getOperand(1));
auto otherType = otherSrcAddr->getType();
if (!otherType.isLoadable(*Inst->getFunction())) {
// If otherType is not loadable, emit a diagnostic since it was used
// on a generic or existential value.
diagnose(Inst->getModule().getASTContext(),
getOpLocation(Inst->getLoc()).getSourceLoc(),
diag::copy_operator_used_on_generic_or_existential_value);
return SILCloner<SILInlineCloner>::visitBuiltinInst(Inst);
}
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
// We stash otherValue in originalOtherValue in case we need to
// perform a writeback.
auto opLoc = getOpLocation(Inst->getLoc());
assert(otherType.isAddress());
// Perform a load_borrow and then copy that.
SILValue otherValue =
getBuilder().emitLoadBorrowOperation(opLoc, otherSrcAddr);
auto *mvi = getBuilder().createExplicitCopyValue(opLoc, otherValue);
getBuilder().emitStoreValueOperation(opLoc, mvi, otherResultAddr,
StoreOwnershipQualifier::Init);
// End the borrowed value.
getBuilder().emitEndBorrowOperation(opLoc, otherValue);
// We know that Inst returns a tuple value that isn't used by anything
// else, so this /should/ be safe.
return recordClonedInstruction(Inst, mvi);
}
}
}
return SILCloner<SILInlineCloner>::visitBuiltinInst(Inst);
}
//===----------------------------------------------------------------------===//
// Cost Model
//===----------------------------------------------------------------------===//

View File

@@ -266,31 +266,11 @@ extension String {
}
#endif
/// Takes in a value at +0 and performs a Builtin.copy upon it.
///
/// IMPLEMENTATION NOTES: During transparent inlining, Builtin.copy becomes the
/// explicit_copy_value instruction if we are inlining into a context where the
/// specialized type is loadable. If the transparent function is called in a
/// context where the inlined function specializes such that the specialized
/// type is still not loadable, the compiler aborts (a). Once we have opaque
/// values, this restriction will be lifted since after that address only types
/// at SILGen time will be loadable objects.
///
/// (a). This is implemented by requiring that Builtin.copy only be called
/// within a function marked with the semantic tag "lifetimemanagement.copy"
/// which conveniently is only the function we are defining here: _copy.
///
/// NOTE: We mark this _alwaysEmitIntoClient to ensure that we are not creating
/// new ABI that the stdlib must maintain if a user calls this ignoring the '_'
/// implying it is stdlib SPI.
@available(*, deprecated, message: "Use the copy operator")
@_alwaysEmitIntoClient
@inlinable
@_transparent
@_semantics("lifetimemanagement.copy")
public func _copy<T>(_ value: T) -> T {
#if $BuiltinCopy
Builtin.copy(value)
#else
value
#endif
copy value
}

View File

@@ -8,93 +8,58 @@ import Swift
class Klass {}
// NOTE:
//
// One will notice in these tests that we are not promoting away the input
// argument to _copy. This is because PredictableMemOpts does not know how to
// handle store_borrow. With time, that will happen and this unfortunateness
// will go away. At -O this problem will not exist though.
//
// That being said, this is actually not a big issue for the usage of this in
// dataflow based OSSA checking since the store_borrow will always just be
// treated as a +0 use of any owned value we are tracking, so it will not be
// treated as a lifetime ending use (albeit it would be considered an
// escaping use).
// CHECK-LABEL: sil [ossa] @$s8moveonly7useCopyyAA5KlassCADF : {{.*}} {
// CHECK: bb0([[ARG:%.*]] :
// CHECK-NEXT: debug_value
// CHECK-NEXT: [[COPY:%[^,]+]] = copy_value [[ARG]]
// CHECK-NEXT: [[EXPLICIT_COPY:%[^,]+]] = explicit_copy_value [[COPY]]
// CHECK-NEXT: destroy_value [[COPY]]
// CHECK-NEXT: return [[EXPLICIT_COPY]]
// CHECK-LABEL: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
// CHECK-LABEL: sil [ossa] @$s8moveonly7useCopyyAA5KlassCADF : $@convention(thin) (@guaranteed Klass) -> @owned Klass {
// CHECK: bb0([[ARG:%.*]] :
// CHECK-NEXT: debug_value
// CHECK-NEXT: [[RESULT_ADDR:%.*]] = alloc_stack $Klass
// CHECK-NEXT: [[INPUT_ADDR:%.*]] = alloc_stack $Klass
// CHECK-NEXT: [[SB:%.*]] = store_borrow [[ARG]] to [[INPUT_ADDR]]
// CHECK-NEXT: // function_ref _copy<A>(_:)
// CHECK-NEXT: [[COPY:%.*]] = function_ref @$ss5_copyyxxlF :
// CHECK-NEXT: [[APPLY_RESULT:%.*]] = apply [[COPY]]<Klass>([[RESULT_ADDR]], [[SB]])
// CHECK-NEXT: end_borrow
// CHECK-NEXT: dealloc_stack [[INPUT_ADDR]]
// CHECK-NEXT: [[RELOADED_VALUE:%.*]] = load [take] [[RESULT_ADDR]]
// CHECK-NEXT: dealloc_stack [[RESULT_ADDR]]
// CHECK-NEXT: return [[RELOADED_VALUE]]
// CHECK: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
// CHECK-SIL-LABEL: sil @$s8moveonly7useCopyyAA5KlassCADF : {{.*}} {
// CHECK-SIL: bb0([[ARG:%.*]] : $Klass):
// CHECK-SIL-NEXT: debug_value
// CHECK-SIL-NEXT: strong_retain [[ARG]]
// CHECK-SIL-NEXT: strong_retain [[ARG]]
// CHECK-SIL-NEXT: strong_release [[ARG]]
// CHECK-SIL-NEXT: return [[ARG]] : $Klass
// CHECK-SIL-LABEL: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
// CHECK-SIL-LABEL: sil @$s8moveonly7useCopyyAA5KlassCADF : $@convention(thin) (@guaranteed Klass) -> @owned Klass {
// CHECK-SIL: bb0([[ARG:%.*]] : $Klass):
// CHECK-SIL-NEXT: debug_value
// CHECK-SIL-NEXT: [[INPUT:%.*]] = alloc_stack [lexical] $Klass
// CHECK-SIL-NEXT: store [[ARG]] to [[INPUT]]
// CHECK-SIL-NEXT: [[VALUE:%[0-9][0-9]*]] = load [[INPUT]]{{.*}}
// CHECK-SIL-NEXT: strong_retain [[VALUE]]
// CHECK-SIL-NEXT: strong_retain [[VALUE]]
// CHECK-SIL-NEXT: strong_release [[VALUE]]
// CHECK-SIL-NEXT: dealloc_stack [[INPUT]] : $*Klass
// CHECK-SIL-NEXT: return [[VALUE]] : $Klass
// CHECK-SIL: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
// CHECK-SIL-OPT-LABEL: sil {{.*}}@$s8moveonly7useCopyyAA5KlassCADF : $@convention(thin) (@guaranteed Klass) -> @owned Klass {
// CHECK-SIL-OPT: bb0([[ARG:%.*]] : $Klass):
// CHECK-SIL-OPT-NEXT: debug_value
// CHECK-SIL-OPT-NEXT: strong_retain [[ARG]]
// CHECK-SIL-OPT-NEXT: return [[ARG]]
// CHECK-SIL-OPT: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
// CHECK-SIL-OPT-LABEL: sil {{.*}}@$s8moveonly7useCopyyAA5KlassCADF : {{.*}} {
// CHECK-SIL-OPT: bb0([[ARG:%.*]] : $Klass):
// CHECK-SIL-OPT-NEXT: debug_value
// CHECK-SIL-OPT-NEXT: strong_retain [[ARG]]
// CHECK-SIL-OPT-NEXT: return [[ARG]]
// CHECK-SIL-OPT-LABEL: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
public func useCopy(_ k: Klass) -> Klass {
_copy(k)
copy k
}
// CHECK-LABEL: sil [ossa] @$s8moveonly7useCopyyxxRlzClF : $@convention(thin) <T where T : AnyObject> (@guaranteed T) -> @owned T {
// CHECK: bb0([[ARG:%.*]] :
// CHECK-NEXT: debug_value
// CHECK-NEXT: [[RESULT_ADDR:%.*]] = alloc_stack $T
// CHECK-NEXT: [[INPUT_ADDR:%.*]] = alloc_stack $T
// CHECK-NEXT: [[SB:%.*]] = store_borrow [[ARG]] to [[INPUT_ADDR]]
// CHECK-NEXT: // function_ref _copy<A>(_:)
// CHECK-NEXT: [[COPY:%.*]] = function_ref @$ss5_copyyxxlF :
// CHECK-NEXT: [[APPLY_RESULT:%.*]] = apply [[COPY]]<T>([[RESULT_ADDR]], [[SB]])
// CHECK-NEXT: end_borrow
// CHECK-NEXT: dealloc_stack [[INPUT_ADDR]]
// CHECK-NEXT: [[RELOADED_VALUE:%.*]] = load [take] [[RESULT_ADDR]]
// CHECK-NEXT: dealloc_stack [[RESULT_ADDR]]
// CHECK-NEXT: return [[RELOADED_VALUE]]
// CHECK: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
// CHECK: bb0([[ARG:%.*]] :
// CHECK-NEXT: debug_value
// CHECK-NEXT: [[COPY:%[^,]+]] = copy_value [[ARG]]
// CHECK-NEXT: [[EXPLICIT_COPY:%[^,]+]] = explicit_copy_value [[COPY]]
// CHECK-NEXT: destroy_value [[COPY]]
// CHECK-NEXT: return [[EXPLICIT_COPY]]
// CHECK-LABEL: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
// CHECK-SIL-LABEL: sil @$s8moveonly7useCopyyxxRlzClF : $@convention(thin) <T where T : AnyObject> (@guaranteed T) -> @owned T {
// CHECK-SIL: bb0([[ARG:%.*]] :
// CHECK-SIL-NEXT: debug_value
// CHECK-SIL-NEXT: [[INPUT_TO_BE_ELIMINATED:%.*]] = alloc_stack [lexical] $T
// CHECK-SIL-NEXT: store [[ARG]] to [[INPUT]] : $*T
// CHECK-SIL-NEXT: [[VALUE:%.*]] = load [[INPUT]] : $*T
// CHECK-SIL-NEXT: strong_retain [[VALUE]]
// CHECK-SIL-NEXT: strong_retain [[VALUE]]
// CHECK-SIL-NEXT: strong_release [[VALUE]]
// CHECK-SIL-NEXT: dealloc_stack [[INPUT]]
// CHECK-SIL-NEXT: return [[VALUE]] : $T
// CHECK-SIL: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
// CHECK-SIL-LABEL: sil @$s8moveonly7useCopyyxxRlzClF : {{.*}} {
// CHECK-SIL: bb0([[ARG:%.*]] :
// CHECK-SIL-NEXT: debug_value
// CHECK-SIL-NEXT: strong_retain [[ARG]]
// CHECK-SIL-NEXT: strong_retain [[ARG]]
// CHECK-SIL-NEXT: strong_release [[ARG]]
// CHECK-SIL-NEXT: return [[ARG]]
// CHECK-SIL-LABEL: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
// CHECK-SIL-OPT-LABEL: sil {{.*}}@$s8moveonly7useCopyyxxRlzClF : $@convention(thin) <T where T : AnyObject> (@guaranteed T) -> @owned T {
// CHECK-SIL-OPT: bb0([[ARG:%.*]] :
// CHECK-SIL-OPT-NEXT: debug_value
// CHECK-SIL-OPT-NEXT: strong_retain [[ARG]]
// CHECK-SIL-OPT-NEXT: return [[ARG]] : $T
// CHECK-SIL-OPT: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
// CHECK-SIL-OPT-LABEL: sil {{.*}}@$s8moveonly7useCopyyxxRlzClF : {{.*}} {
// CHECK-SIL-OPT: bb0([[ARG:%.*]] :
// CHECK-SIL-OPT-NEXT: debug_value
// CHECK-SIL-OPT-NEXT: strong_retain [[ARG]]
// CHECK-SIL-OPT-NEXT: return [[ARG]] : $T
// CHECK-SIL-OPT-LABEL: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
public func useCopy<T : AnyObject>(_ k: T) -> T {
_copy(k)
copy k
}

View File

@@ -3,5 +3,5 @@
import Swift
func addressOnlyCopy<T>(t: T) -> T {
_copy(t) // expected-error {{copy() used on a generic or existential value}}
copy t
}

View File

@@ -13,9 +13,6 @@ struct NC: ~Copyable {}
func checkPointerBuiltins(_ ptr: Builtin.RawPointer, _ value: consuming NC) {
Builtin.initialize(value, ptr)
#if ILLEGAL
Builtin.copy(value) // expected-illegal-error {{noncopyable type 'NC' cannot be substituted for copyable generic parameter 'T' in 'copy'}}
#endif
}
func checkArrayBuiltins(_ dest: Builtin.RawPointer, src: Builtin.RawPointer, count: Builtin.Word) {

View File

@@ -34,29 +34,10 @@ class X {}
func consume(_ x : __owned X) {}
func foo(@_noImplicitCopy _ x: __owned X) {
consume(_copy(x))
consume(copy x)
consume(x)
}
// CHECK-LABEL: sil [transparent] [_semantics "lifetimemanagement.copy"] @_copy : {{.*}} {
// CHECK: {{bb[0-9]+}}([[OUT_ADDR:%[^,]+]] : $*T, [[IN_ADDR:%[^,]+]] : $*T):
// CHECK: [[TMP_ADDR:%[^,]+]] = alloc_stack $T
// CHECK: copy_addr [[IN_ADDR]] to [init] [[TMP_ADDR]] : $*T
// CHECK: builtin "copy"<T>([[OUT_ADDR]] : $*T, [[TMP_ADDR]] : $*T) : $()
// CHECK: dealloc_stack [[TMP_ADDR]] : $*T
// CHECK: return {{%[^,]+}} : $()
// CHECK-LABEL: } // end sil function '_copy'
@_transparent
@_semantics("lifetimemanagement.copy")
@_silgen_name("_copy")
public func _copy<T>(_ value: T) -> T {
#if $BuiltinCopy
Builtin.copy(value)
#else
value
#endif
}
// CHECK-LABEL: sil hidden @getRawPointer : {{.*}} {
// CHECK: {{bb[0-9]+}}([[ADDR:%[^,]+]] : $*T):
// CHECK: [[PTR:%[^,]+]] = address_to_pointer [stack_protection] [[ADDR]]

View File

@@ -1,18 +1,24 @@
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-move-only)
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-move-only -enable-builtin-module)
// REQUIRES: executable_test
import StdlibUnittest
import Builtin
class Klass {}
var suite = TestSuite("LifetimeManagement")
suite.test("copy") {
suite.test("_copy") {
let k = Klass()
expectTrue(k === _copy(k))
}
suite.test("copy") {
let k = Klass()
expectTrue(k === copy k)
}
suite.test("move") {
let k = Klass()
let k2 = k
@@ -20,3 +26,19 @@ suite.test("move") {
}
runAllTests()
// TODO: Remove. Only exists to avoid a reverse condfail.
// rdar://127516085 (Complete removal of Builtin.copy)
func _oldCopy<T>(_ value: T) -> T {
#if $BuiltinCopy
Builtin.copy(value)
#else
value
#endif
}
suite.test("_oldCopy") {
let k = Klass()
expectTrue(k === _oldCopy(k))
}