Skip to content

Enable moveonly / noncopyable types by default #64106

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 9 commits into from
Mar 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
3 changes: 3 additions & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,9 @@ class ASTContext final {
FuncDecl *getMakeInvocationEncoderOnDistributedActorSystem(
AbstractFunctionDecl *thunk) const;

/// Indicates whether move-only / noncopyable types are supported.
bool supportsMoveOnlyTypes() const;

// Retrieve the declaration of
// DistributedInvocationEncoder.recordGenericSubstitution(_:).
//
Expand Down
9 changes: 4 additions & 5 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6744,11 +6744,6 @@ ERROR(experimental_moveonly_feature_can_only_be_used_when_enabled,
none, "Can not use feature when experimental move only is disabled! Pass"
" the frontend flag -enable-experimental-move-only to swift to enable "
"the usage of this language feature", ())
ERROR(experimental_moveonly_feature_can_only_be_imported_when_enabled,
none, "Can not import module %0 that uses move only features when "
"experimental move only is disabled! Pass the frontend flag "
"-enable-experimental-move-only to swift to enable the usage of this "
"language feature", (Identifier))
ERROR(noimplicitcopy_attr_valid_only_on_local_let_params,
none, "'@_noImplicitCopy' attribute can only be applied to local lets and params", ())
ERROR(noimplicitcopy_attr_invalid_in_generic_context,
Expand Down Expand Up @@ -6831,6 +6826,10 @@ ERROR(move_expression_not_passed_lvalue,none,
ERROR(borrow_expression_not_passed_lvalue,none,
"'borrow' can only be applied to lvalues", ())

ERROR(moveOnly_requires_lexical_lifetimes,none,
"noncopyable types require lexical borrow scopes "
"(add -enable-lexical-borrow-scopes=true)", ())

//------------------------------------------------------------------------------
// MARK: #_hasSymbol
//------------------------------------------------------------------------------
Expand Down
19 changes: 14 additions & 5 deletions include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -1484,11 +1484,20 @@ template <typename ImplClass>
void SILCloner<ImplClass>::visitExplicitCopyAddrInst(
ExplicitCopyAddrInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
recordClonedInstruction(
Inst, getBuilder().createExplicitCopyAddr(
getOpLocation(Inst->getLoc()), getOpValue(Inst->getSrc()),
getOpValue(Inst->getDest()), Inst->isTakeOfSrc(),
Inst->isInitializationOfDest()));
if (!getBuilder().hasOwnership()) {
recordClonedInstruction(
Inst, getBuilder().createCopyAddr(
getOpLocation(Inst->getLoc()), getOpValue(Inst->getSrc()),
getOpValue(Inst->getDest()), Inst->isTakeOfSrc(),
Inst->isInitializationOfDest()));
} else {
// preserve the explicit_*
recordClonedInstruction(
Inst, getBuilder().createExplicitCopyAddr(
getOpLocation(Inst->getLoc()), getOpValue(Inst->getSrc()),
getOpValue(Inst->getDest()), Inst->isTakeOfSrc(),
Inst->isInitializationOfDest()));
}
}

template <typename ImplClass>
Expand Down
5 changes: 5 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6363,3 +6363,8 @@ ASTContext::lookupExecutablePluginByModuleName(Identifier moduleName) {

return plugin.get();
}

bool ASTContext::supportsMoveOnlyTypes() const {
// currently the only thing holding back whether the types can appear is this.
return SILOpts.LexicalLifetimes != LexicalLifetimesOption::Off;
}
20 changes: 20 additions & 0 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2822,6 +2822,18 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"'MoveOnly' types can only be copied in Raw SIL?!");
}

void checkExplicitCopyAddrInst(ExplicitCopyAddrInst *ecai) {
require(F.hasOwnership(), "explicit_copy_* is only valid in OSSA.");
require(ecai->getSrc()->getType().isAddress(),
"Src value should be lvalue");
require(ecai->getDest()->getType().isAddress(),
"Dest address should be lvalue");
requireSameType(ecai->getDest()->getType(), ecai->getSrc()->getType(),
"Store operand type and dest type mismatch");
require(F.isTypeABIAccessible(ecai->getDest()->getType()),
"cannot directly copy type with inaccessible ABI");
}

void checkMarkUnresolvedMoveAddrInst(MarkUnresolvedMoveAddrInst *SI) {
require(F.hasOwnership(), "Only valid in OSSA.");
require(F.getModule().getStage() == SILStage::Raw, "Only valid in Raw SIL");
Expand Down Expand Up @@ -2861,6 +2873,14 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"'MoveOnly' types can only be copied in Raw SIL?!");
}

void checkExplicitCopyValueInst(ExplicitCopyValueInst *I) {
require(F.hasOwnership(), "explicit_copy_* is only valid in OSSA.");
require(I->getOperand()->getType().isObject(),
"Source value should be an object value");
require(!I->getOperand()->getType().isTrivial(*I->getFunction()),
"Source value should be non-trivial");
}

void checkDestroyValueInst(DestroyValueInst *I) {
require(I->getOperand()->getType().isObject(),
"Source value should be an object value");
Expand Down
75 changes: 29 additions & 46 deletions lib/SILGen/SILGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -620,8 +620,7 @@ class LetValueInitialization : public Initialization {
public:
LetValueInitialization(VarDecl *vd, SILGenFunction &SGF) : vd(vd) {
const TypeLowering *lowering = nullptr;
if (SGF.getASTContext().LangOpts.Features.count(Feature::MoveOnly) &&
vd->isNoImplicitCopy()) {
if (vd->isNoImplicitCopy()) {
lowering = &SGF.getTypeLowering(
SILMoveOnlyWrappedType::get(vd->getType()->getCanonicalType()));
} else {
Expand Down Expand Up @@ -661,8 +660,7 @@ class LetValueInitialization : public Initialization {

// Make sure that we have a non-address only type when binding a
// @_noImplicitCopy let.
if (SGF.getASTContext().LangOpts.Features.count(Feature::MoveOnly) &&
lowering->isAddressOnly() && vd->isNoImplicitCopy()) {
if (lowering->isAddressOnly() && vd->isNoImplicitCopy()) {
auto d = diag::noimplicitcopy_used_on_generic_or_existential;
diagnose(SGF.getASTContext(), vd->getLoc(), d);
}
Expand Down Expand Up @@ -740,10 +738,6 @@ class LetValueInitialization : public Initialization {
SILValue value, bool wasPlusOne) {
// If we have none...
if (value->getOwnershipKind() == OwnershipKind::None) {
// If we don't have move only features enabled, just return, we are done.
if (!SGF.getASTContext().LangOpts.Features.count(Feature::MoveOnly))
return value;

// Then check if we have a pure move only type. In that case, we need to
// insert a no implicit copy
if (value->getType().isPureMoveOnly()) {
Expand All @@ -768,15 +762,6 @@ class LetValueInitialization : public Initialization {
MarkMustCheckInst::CheckKind::ConsumableAndAssignable);
}

// Then if we don't have move only, just perform a lexical borrow if the
// lifetime is lexical.
if (!SGF.getASTContext().LangOpts.Features.count(Feature::MoveOnly)) {
if (SGF.F.getLifetime(vd, value->getType()).isLexical())
return SGF.B.createBeginBorrow(PrologueLoc, value, /*isLexical*/ true);
else
return value;
}

// Otherwise, we need to perform some additional processing. First, if we
// have an owned moveonly value that had a cleanup, then create a move_value
// that acts as a consuming use of the value. The reason why we want this is
Expand Down Expand Up @@ -2136,35 +2121,22 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) {
return;
}

if (getASTContext().LangOpts.hasFeature(Feature::MoveOnly)) {
if (auto *mvi = dyn_cast<MarkMustCheckInst>(Val.getDefiningInstruction())) {
if (mvi->hasMoveCheckerKind()) {
if (auto *cvi = dyn_cast<CopyValueInst>(mvi->getOperand())) {
if (auto *bbi = dyn_cast<BeginBorrowInst>(cvi->getOperand())) {
if (bbi->isLexical()) {
B.emitDestroyValueOperation(silLoc, mvi);
B.createEndBorrow(silLoc, bbi);
B.emitDestroyValueOperation(silLoc, bbi->getOperand());
return;
}
}
}

if (auto *copyToMove = dyn_cast<CopyableToMoveOnlyWrapperValueInst>(
mvi->getOperand())) {
if (auto *cvi = dyn_cast<CopyValueInst>(copyToMove->getOperand())) {
if (auto *bbi = dyn_cast<BeginBorrowInst>(cvi->getOperand())) {
if (bbi->isLexical()) {
B.emitDestroyValueOperation(silLoc, mvi);
B.createEndBorrow(silLoc, bbi);
B.emitDestroyValueOperation(silLoc, bbi->getOperand());
return;
}
}
if (auto *mvi = dyn_cast<MarkMustCheckInst>(Val.getDefiningInstruction())) {
if (mvi->hasMoveCheckerKind()) {
if (auto *cvi = dyn_cast<CopyValueInst>(mvi->getOperand())) {
if (auto *bbi = dyn_cast<BeginBorrowInst>(cvi->getOperand())) {
if (bbi->isLexical()) {
B.emitDestroyValueOperation(silLoc, mvi);
B.createEndBorrow(silLoc, bbi);
B.emitDestroyValueOperation(silLoc, bbi->getOperand());
return;
}
}
}

if (auto *cvi = dyn_cast<ExplicitCopyValueInst>(mvi->getOperand())) {
if (auto *copyToMove = dyn_cast<CopyableToMoveOnlyWrapperValueInst>(
mvi->getOperand())) {
if (auto *cvi = dyn_cast<CopyValueInst>(copyToMove->getOperand())) {
if (auto *bbi = dyn_cast<BeginBorrowInst>(cvi->getOperand())) {
if (bbi->isLexical()) {
B.emitDestroyValueOperation(silLoc, mvi);
Expand All @@ -2174,15 +2146,26 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) {
}
}
}
}

// Handle trivial arguments.
if (auto *move = dyn_cast<MoveValueInst>(mvi->getOperand())) {
if (move->isLexical()) {
if (auto *cvi = dyn_cast<ExplicitCopyValueInst>(mvi->getOperand())) {
if (auto *bbi = dyn_cast<BeginBorrowInst>(cvi->getOperand())) {
if (bbi->isLexical()) {
B.emitDestroyValueOperation(silLoc, mvi);
B.createEndBorrow(silLoc, bbi);
B.emitDestroyValueOperation(silLoc, bbi->getOperand());
return;
}
}
}

// Handle trivial arguments.
if (auto *move = dyn_cast<MoveValueInst>(mvi->getOperand())) {
if (move->isLexical()) {
B.emitDestroyValueOperation(silLoc, mvi);
return;
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2400,7 +2400,7 @@ class ConsumeOperatorCopyableAddressesCheckerPass
auto &astContext = fn->getASTContext();

// Only run this pass if the move only language feature is enabled.
if (!astContext.LangOpts.Features.contains(Feature::MoveOnly))
if (!astContext.supportsMoveOnlyTypes())
return;

// Don't rerun diagnostics on deserialized functions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ class ConsumeOperatorCopyableValuesCheckerPass : public SILFunctionTransform {
auto *fn = getFunction();

// Only run this pass if the move only language feature is enabled.
if (!fn->getASTContext().LangOpts.Features.contains(Feature::MoveOnly))
if (!fn->getASTContext().supportsMoveOnlyTypes())
return;

// Don't rerun diagnostics on deserialized functions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class MoveOnlyAddressCheckerTesterPass : public SILFunctionTransform {
auto *fn = getFunction();

// Only run this pass if the move only language feature is enabled.
if (!fn->getASTContext().LangOpts.Features.contains(Feature::MoveOnly))
if (!fn->getASTContext().supportsMoveOnlyTypes())
return;

// Don't rerun diagnostics on deserialized functions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class MoveOnlyBorrowToDestructureTransformPass : public SILFunctionTransform {
auto *fn = getFunction();

// Only run this pass if the move only language feature is enabled.
if (!fn->getASTContext().LangOpts.Features.contains(Feature::MoveOnly))
if (!fn->getASTContext().supportsMoveOnlyTypes())
return;

// Don't rerun diagnostics on deserialized functions.
Expand Down
2 changes: 1 addition & 1 deletion lib/SILOptimizer/Mandatory/MoveOnlyChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class MoveOnlyCheckerPass : public SILFunctionTransform {
auto *fn = getFunction();

// Only run this pass if the move only language feature is enabled.
if (!fn->getASTContext().LangOpts.Features.contains(Feature::MoveOnly))
if (!fn->getASTContext().supportsMoveOnlyTypes())
return;

// Don't rerun diagnostics on deserialized functions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class MoveOnlyObjectCheckerTesterPass : public SILFunctionTransform {
auto *fn = getFunction();

// Only run this pass if the move only language feature is enabled.
if (!fn->getASTContext().LangOpts.Features.contains(Feature::MoveOnly))
if (!fn->getASTContext().supportsMoveOnlyTypes())
return;

// Don't rerun diagnostics on deserialized functions.
Expand Down
15 changes: 11 additions & 4 deletions lib/SILOptimizer/Mandatory/MoveOnlyUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,12 @@ bool swift::siloptimizer::cleanupNonCopyableCopiesAfterEmittingDiagnostic(
auto *inst = &*ii;
++ii;

// Convert load [copy] -> load_borrow + explicit_copy_value.
// Convert load [copy] *MoveOnly -> load_borrow + explicit_copy_value.
if (auto *li = dyn_cast<LoadInst>(inst)) {
if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) {
if (!li->getType().isMoveOnly())
continue;

SILBuilderWithScope builder(li);
auto *lbi = builder.createLoadBorrow(li->getLoc(), li->getOperand());
auto *cvi = builder.createExplicitCopyValue(li->getLoc(), lbi);
Expand All @@ -129,10 +132,13 @@ bool swift::siloptimizer::cleanupNonCopyableCopiesAfterEmittingDiagnostic(
}
}

// Convert copy_addr !take of src to its explicit value form so we don't
// error.
// Convert copy_addr !take MoveOnly ... -> explicit_copy_addr ...same...
// so we don't error.
if (auto *copyAddr = dyn_cast<CopyAddrInst>(inst)) {
if (!copyAddr->isTakeOfSrc()) {
if (!copyAddr->getSrc()->getType().isMoveOnly())
continue;

SILBuilderWithScope builder(copyAddr);
builder.createExplicitCopyAddr(
copyAddr->getLoc(), copyAddr->getSrc(), copyAddr->getDest(),
Expand All @@ -143,10 +149,11 @@ bool swift::siloptimizer::cleanupNonCopyableCopiesAfterEmittingDiagnostic(
}
}

// Convert any copy_value of move_only type to explicit copy value.
// Convert any copy_value of MoveOnly type -> explicit_copy_value.
if (auto *cvi = dyn_cast<CopyValueInst>(inst)) {
if (!cvi->getOperand()->getType().isMoveOnly())
continue;

SILBuilderWithScope b(cvi);
auto *expCopy =
b.createExplicitCopyValue(cvi->getLoc(), cvi->getOperand());
Expand Down
23 changes: 0 additions & 23 deletions lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,31 +428,13 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
}

void checkMoveExpr(MoveExpr *moveExpr) {
// Make sure the MoveOnly feature is set. If not, error.
// This should not currently be reached because the parse should ignore
// the _move keyword unless the feature flag is set.
if (!Ctx.LangOpts.hasFeature(Feature::MoveOnly)) {
auto error =
diag::experimental_moveonly_feature_can_only_be_used_when_enabled;
Ctx.Diags.diagnose(moveExpr->getLoc(), error);
}

if (!isa<DeclRefExpr>(moveExpr->getSubExpr())) {
Ctx.Diags.diagnose(moveExpr->getLoc(),
diag::move_expression_not_passed_lvalue);
}
}

void checkBorrowExpr(BorrowExpr *borrowExpr) {
// Make sure the MoveOnly feature is set. If not, error.
// This should not currently be reached because the parse should ignore
// the _move keyword unless the feature flag is set.
if (!Ctx.LangOpts.hasFeature(Feature::MoveOnly)) {
auto error =
diag::experimental_moveonly_feature_can_only_be_used_when_enabled;
Ctx.Diags.diagnose(borrowExpr->getLoc(), error);
}

// Allow for a chain of member_ref exprs that end in a decl_ref expr.
auto *subExpr = borrowExpr->getSubExpr();
while (auto *memberRef = dyn_cast<MemberRefExpr>(subExpr))
Expand Down Expand Up @@ -6177,11 +6159,6 @@ bool swift::diagnoseUnhandledThrowsInAsyncContext(DeclContext *dc,

void swift::diagnoseCopyableTypeContainingMoveOnlyType(
NominalTypeDecl *copyableNominalType) {
// If we don't have move only enabled, bail early.
if (!copyableNominalType->getASTContext().LangOpts.Features.contains(
Feature::MoveOnly))
return;

// If we already have a move only type, just bail, we have no further work to
// do.
if (copyableNominalType->isMoveOnly())
Expand Down
8 changes: 2 additions & 6 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2159,12 +2159,8 @@ void AttributeChecker::visitFinalAttr(FinalAttr *attr) {
}

void AttributeChecker::visitMoveOnlyAttr(MoveOnlyAttr *attr) {
if (!D->getASTContext().LangOpts.hasFeature(Feature::MoveOnly)) {
auto error =
diag::experimental_moveonly_feature_can_only_be_used_when_enabled;
diagnoseAndRemoveAttr(attr, error);
return;
}
if (!D->getASTContext().supportsMoveOnlyTypes())
D->diagnose(diag::moveOnly_requires_lexical_lifetimes);

if (isa<StructDecl>(D) || isa<EnumDecl>(D))
return;
Expand Down
Loading