Skip to content

Commit f503da1

Browse files
committed
[copy-operator] Add support for the copy operator in preparation for making consuming and borrowing no implicit copy.
Some notes: 1. I implemented this as a contextual keyword that can only apply directly to lvalues. This ensures that we can still call functions called copy, define variables named copy, etc. I added tests for both the c++ and swift-syntax based parsers to validate this. So there shouldn't be any source breaks. 2. I did a little bit of type checker work to ensure that we do not treat copy_expr's result as an lvalue. Otherwise, one could call mutating functions on it or assign to it, which we do not want since the result of copy_value is 3. As expected, by creating a specific expr, I was able to have much greater control of the SILGen codegen and thus eliminate extraneous copies and other weirdness than if we used a function and had to go through SILGenApply. rdar://101862423
1 parent f16bf16 commit f503da1

21 files changed

+887
-1
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,8 @@ ERROR(expected_expr_after_await, none,
13861386
"expected expression after 'await'", ())
13871387
ERROR(expected_expr_after_move, none,
13881388
"expected expression after 'consume'", ())
1389+
ERROR(expected_expr_after_copy, none,
1390+
"expected expression after 'copy'", ())
13891391
ERROR(expected_expr_after_borrow, none,
13901392
"expected expression after '_borrow'", ())
13911393

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6996,6 +6996,10 @@ ERROR(move_expression_not_passed_lvalue,none,
69966996
"'consume' can only be applied to lvalues", ())
69976997
ERROR(borrow_expression_not_passed_lvalue,none,
69986998
"'borrow' can only be applied to lvalues", ())
6999+
ERROR(copy_expression_not_passed_lvalue,none,
7000+
"'copy' can only be applied to lvalues", ())
7001+
ERROR(copy_expression_cannot_be_used_with_noncopyable_types,none,
7002+
"'copy' cannot be applied to noncopyable types", ())
69997003

70007004
ERROR(moveOnly_requires_lexical_lifetimes,none,
70017005
"noncopyable types require lexical borrow scopes "

include/swift/AST/Expr.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2077,6 +2077,33 @@ class MoveExpr final : public IdentityExpr {
20772077
static bool classof(const Expr *e) { return e->getKind() == ExprKind::Move; }
20782078
};
20792079

2080+
/// CopyExpr - A 'copy' surrounding an lvalue expression marking the lvalue as
2081+
/// needing a semantic copy. Used to force a copy of a no implicit copy type.
2082+
class CopyExpr final : public Expr {
2083+
Expr *SubExpr;
2084+
SourceLoc CopyLoc;
2085+
2086+
public:
2087+
CopyExpr(SourceLoc copyLoc, Expr *sub, Type type = Type(),
2088+
bool implicit = false)
2089+
: Expr(ExprKind::Copy, implicit, type), SubExpr(sub), CopyLoc(copyLoc) {}
2090+
2091+
static CopyExpr *createImplicit(ASTContext &ctx, SourceLoc copyLoc, Expr *sub,
2092+
Type type = Type()) {
2093+
return new (ctx) CopyExpr(copyLoc, sub, type, /*implicit=*/true);
2094+
}
2095+
2096+
SourceLoc getLoc() const { return CopyLoc; }
2097+
2098+
Expr *getSubExpr() const { return SubExpr; }
2099+
void setSubExpr(Expr *E) { SubExpr = E; }
2100+
2101+
SourceLoc getStartLoc() const { return CopyLoc; }
2102+
SourceLoc getEndLoc() const { return getSubExpr()->getEndLoc(); }
2103+
2104+
static bool classof(const Expr *e) { return e->getKind() == ExprKind::Copy; }
2105+
};
2106+
20802107
/// BorrowExpr - A 'borrow' surrounding an lvalue/accessor expression at an
20812108
/// apply site marking the lvalue/accessor as being borrowed when passed to the
20822109
/// callee.

include/swift/AST/ExprNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ ABSTRACT_EXPR(Identity, Expr)
111111
EXPR(Borrow, IdentityExpr)
112112
EXPR(UnresolvedMemberChainResult, IdentityExpr)
113113
EXPR_RANGE(Identity, Paren, UnresolvedMemberChainResult)
114+
EXPR(Copy, Expr)
114115
ABSTRACT_EXPR(AnyTry, Expr)
115116
EXPR(Try, AnyTryExpr)
116117
EXPR(ForceTry, AnyTryExpr)

lib/AST/ASTDumper.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2200,6 +2200,12 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
22002200
printRec(E->getSubExpr());
22012201
PrintWithColorRAII(OS, ParenthesisColor) << ')';
22022202
}
2203+
void visitCopyExpr(CopyExpr *E) {
2204+
printCommon(E, "copy_expr");
2205+
OS << '\n';
2206+
printRec(E->getSubExpr());
2207+
PrintWithColorRAII(OS, ParenthesisColor) << ')';
2208+
}
22032209
void visitBorrowExpr(BorrowExpr *E) {
22042210
printCommon(E, "borrow_expr");
22052211
OS << '\n';

lib/AST/ASTPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5001,6 +5001,11 @@ void PrintAST::visitMoveExpr(MoveExpr *expr) {
50015001
visit(expr->getSubExpr());
50025002
}
50035003

5004+
void PrintAST::visitCopyExpr(CopyExpr *expr) {
5005+
Printer << "copy ";
5006+
visit(expr->getSubExpr());
5007+
}
5008+
50045009
void PrintAST::visitBorrowExpr(BorrowExpr *expr) {
50055010
Printer << "borrow ";
50065011
visit(expr->getSubExpr());

lib/AST/ASTWalker.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,15 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
690690
}
691691
return nullptr;
692692
}
693+
694+
Expr *visitCopyExpr(CopyExpr *E) {
695+
if (Expr *subExpr = doIt(E->getSubExpr())) {
696+
E->setSubExpr(subExpr);
697+
return E;
698+
}
699+
return nullptr;
700+
}
701+
693702
Expr *visitTupleExpr(TupleExpr *E) {
694703
for (unsigned i = 0, e = E->getNumElements(); i != e; ++i)
695704
if (E->getElement(i)) {

lib/AST/Expr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@ SourceLoc Expr::getLoc() const {
192192
Expr *Expr::getSemanticsProvidingExpr() {
193193
if (auto *IE = dyn_cast<IdentityExpr>(this))
194194
return IE->getSubExpr()->getSemanticsProvidingExpr();
195-
196195
if (auto *TE = dyn_cast<TryExpr>(this))
197196
return TE->getSubExpr()->getSemanticsProvidingExpr();
198197

@@ -372,6 +371,7 @@ ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const {
372371
PASS_THROUGH_REFERENCE(DotSelf, getSubExpr);
373372
PASS_THROUGH_REFERENCE(Await, getSubExpr);
374373
PASS_THROUGH_REFERENCE(Move, getSubExpr);
374+
PASS_THROUGH_REFERENCE(Copy, getSubExpr);
375375
PASS_THROUGH_REFERENCE(Borrow, getSubExpr);
376376
PASS_THROUGH_REFERENCE(Try, getSubExpr);
377377
PASS_THROUGH_REFERENCE(ForceTry, getSubExpr);
@@ -744,6 +744,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const {
744744

745745
case ExprKind::Await:
746746
case ExprKind::Move:
747+
case ExprKind::Copy:
747748
case ExprKind::Borrow:
748749
case ExprKind::Try:
749750
case ExprKind::ForceTry:
@@ -936,6 +937,7 @@ bool Expr::isValidParentOfTypeExpr(Expr *typeExpr) const {
936937
case ExprKind::Paren:
937938
case ExprKind::Await:
938939
case ExprKind::Move:
940+
case ExprKind::Copy:
939941
case ExprKind::Borrow:
940942
case ExprKind::UnresolvedMemberChainResult:
941943
case ExprKind::Try:

lib/Parse/ParseExpr.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,21 @@ ParserResult<Expr> Parser::parseExprSequenceElement(Diag<> message,
422422
return sub;
423423
}
424424

425+
if (Tok.isContextualKeyword("copy") &&
426+
peekToken().isAny(tok::identifier, tok::kw_self, tok::dollarident,
427+
tok::code_complete) &&
428+
!peekToken().isAtStartOfLine()) {
429+
Tok.setKind(tok::contextual_keyword);
430+
431+
SourceLoc copyLoc = consumeToken();
432+
ParserResult<Expr> sub =
433+
parseExprSequenceElement(diag::expected_expr_after_copy, isExprBasic);
434+
if (!sub.isNull()) {
435+
sub = makeParserResult(new (Context) CopyExpr(copyLoc, sub.get()));
436+
}
437+
return sub;
438+
}
439+
425440
if (Context.LangOpts.hasFeature(Feature::OldOwnershipOperatorSpellings)) {
426441
if (Tok.isContextualKeyword("_move")) {
427442
Tok.setKind(tok::contextual_keyword);

lib/SILGen/SILGenBuilder.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,3 +1050,9 @@ void SILGenBuilder::emitCopyAddrOperation(SILLocation loc, SILValue srcAddr,
10501050
auto &lowering = getTypeLowering(srcAddr->getType());
10511051
lowering.emitCopyInto(*this, loc, srcAddr, destAddr, isTake, isInitialize);
10521052
}
1053+
1054+
ManagedValue SILGenBuilder::createExplicitCopyValue(SILLocation loc,
1055+
ManagedValue operand) {
1056+
auto cvi = SILBuilder::createExplicitCopyValue(loc, operand.getValue());
1057+
return SGF.emitManagedRValueWithCleanup(cvi);
1058+
}

lib/SILGen/SILGenBuilder.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ class SILGenBuilder : public SILBuilder {
125125
ManagedValue createFormalAccessCopyValue(SILLocation loc,
126126
ManagedValue originalValue);
127127

128+
using SILBuilder::createExplicitCopyValue;
129+
130+
/// A copy_value operation that to the move checker looks like just a normal
131+
/// liveness use. Used to implement an explicit copy for no implicit copy
132+
/// values.
133+
ManagedValue createExplicitCopyValue(SILLocation Loc, ManagedValue operand);
134+
128135
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
129136
using SILBuilder::createStrongCopy##Name##Value; \
130137
ManagedValue createStrongCopy##Name##Value(SILLocation loc, \

lib/SILGen/SILGenExpr.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@ namespace {
552552
RValue visitLinearToDifferentiableFunctionExpr(
553553
LinearToDifferentiableFunctionExpr *E, SGFContext C);
554554
RValue visitMoveExpr(MoveExpr *E, SGFContext C);
555+
RValue visitCopyExpr(CopyExpr *E, SGFContext C);
555556
RValue visitMacroExpansionExpr(MacroExpansionExpr *E, SGFContext C);
556557
};
557558
} // end anonymous namespace
@@ -6156,6 +6157,64 @@ RValue RValueEmitter::visitMoveExpr(MoveExpr *E, SGFContext C) {
61566157
return RValue(SGF, {optTemp->getManagedAddress()}, subType.getASTType());
61576158
}
61586159

6160+
RValue RValueEmitter::visitCopyExpr(CopyExpr *E, SGFContext C) {
6161+
auto *subExpr = E->getSubExpr();
6162+
auto subASTType = subExpr->getType()->getCanonicalType();
6163+
auto subType = SGF.getLoweredType(subASTType);
6164+
6165+
if (auto *li = dyn_cast<LoadExpr>(subExpr)) {
6166+
6167+
FormalEvaluationScope writeback(SGF);
6168+
LValue lv =
6169+
SGF.emitLValue(li->getSubExpr(), SGFAccessKind::BorrowedAddressRead);
6170+
auto address = SGF.emitAddressOfLValue(subExpr, std::move(lv));
6171+
6172+
if (subType.isLoadable(SGF.F)) {
6173+
// Use a formal access load borrow so this closes in the writeback scope
6174+
// above.
6175+
ManagedValue value = SGF.B.createFormalAccessLoadBorrow(E, address);
6176+
6177+
// We purposely, use a lexical cleanup here so that the cleanup lasts
6178+
// through the formal evaluation scope.
6179+
ManagedValue copy = SGF.B.createExplicitCopyValue(E, value);
6180+
6181+
return RValue(SGF, {copy}, subType.getASTType());
6182+
}
6183+
6184+
auto optTemp = SGF.emitTemporary(E, SGF.getTypeLowering(subType));
6185+
SILValue toAddr = optTemp->getAddressForInPlaceInitialization(SGF, E);
6186+
SGF.B.createExplicitCopyAddr(subExpr, address.getLValueAddress(), toAddr,
6187+
IsNotTake, IsInitialization);
6188+
optTemp->finishInitialization(SGF);
6189+
return RValue(SGF, {optTemp->getManagedAddress()}, subType.getASTType());
6190+
}
6191+
6192+
if (subType.isLoadable(SGF.F)) {
6193+
ManagedValue mv = SGF.emitRValue(subExpr).getAsSingleValue(SGF, subExpr);
6194+
if (mv.getType().isTrivial(SGF.F))
6195+
return RValue(SGF, {mv}, subType.getASTType());
6196+
mv = SGF.B.createExplicitCopyValue(E, mv);
6197+
return RValue(SGF, {mv}, subType.getASTType());
6198+
}
6199+
6200+
// If we aren't loadable, then create a temporary initialization and
6201+
// explicit_copy_addr into that.
6202+
std::unique_ptr<TemporaryInitialization> optTemp;
6203+
optTemp = SGF.emitTemporary(E, SGF.getTypeLowering(subType));
6204+
SILValue toAddr = optTemp->getAddressForInPlaceInitialization(SGF, E);
6205+
assert(!isa<LValueType>(E->getType()->getCanonicalType()) &&
6206+
"Shouldn't see an lvalue type here");
6207+
6208+
ManagedValue mv =
6209+
SGF.emitRValue(subExpr, SGFContext(SGFContext::AllowImmediatePlusZero))
6210+
.getAsSingleValue(SGF, subExpr);
6211+
assert(mv.getType().isAddress());
6212+
SGF.B.createExplicitCopyAddr(subExpr, mv.getValue(), toAddr, IsNotTake,
6213+
IsInitialization);
6214+
optTemp->finishInitialization(SGF);
6215+
return RValue(SGF, {optTemp->getManagedAddress()}, subType.getASTType());
6216+
}
6217+
61596218
RValue RValueEmitter::visitMacroExpansionExpr(MacroExpansionExpr *E,
61606219
SGFContext C) {
61616220
if (auto *rewritten = E->getRewritten()) {

lib/SILGen/SILGenLValue.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenLValue
334334
LValueOptions options);
335335
LValue visitMoveExpr(MoveExpr *e, SGFAccessKind accessKind,
336336
LValueOptions options);
337+
LValue visitCopyExpr(CopyExpr *e, SGFAccessKind accessKind,
338+
LValueOptions options);
337339
LValue visitABISafeConversionExpr(ABISafeConversionExpr *e,
338340
SGFAccessKind accessKind,
339341
LValueOptions options);
@@ -4033,6 +4035,30 @@ LValue SILGenLValue::visitMoveExpr(MoveExpr *e, SGFAccessKind accessKind,
40334035
toAddr->getType().getASTType());
40344036
}
40354037

4038+
LValue SILGenLValue::visitCopyExpr(CopyExpr *e, SGFAccessKind accessKind,
4039+
LValueOptions options) {
4040+
// Do formal evaluation of the base l-value.
4041+
LValue baseLV = visitRec(e->getSubExpr(), SGFAccessKind::BorrowedAddressRead,
4042+
options.forComputedBaseLValue());
4043+
4044+
ManagedValue addr = SGF.emitAddressOfLValue(e, std::move(baseLV));
4045+
4046+
// Now create the temporary and copy our value into there using an explicit
4047+
// copy_value. This ensures that the rest of the move checker views this as a
4048+
// liveness requiring use rather than a copy that must be eliminated.
4049+
auto temp =
4050+
SGF.emitFormalAccessTemporary(e, SGF.F.getTypeLowering(addr.getType()));
4051+
auto toAddr = temp->getAddressForInPlaceInitialization(SGF, e);
4052+
SGF.B.createExplicitCopyAddr(e, addr.getValue(), toAddr, IsNotTake,
4053+
IsInitialization);
4054+
temp->finishInitialization(SGF);
4055+
4056+
// Now return the temporary in a value component.
4057+
return LValue::forValue(SGFAccessKind::BorrowedAddressRead,
4058+
temp->getManagedAddress(),
4059+
toAddr->getType().getASTType());
4060+
}
4061+
40364062
LValue SILGenLValue::visitABISafeConversionExpr(ABISafeConversionExpr *e,
40374063
SGFAccessKind accessKind,
40384064
LValueOptions options) {

lib/Sema/CSApply.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3525,6 +3525,27 @@ namespace {
35253525
return expr;
35263526
}
35273527

3528+
Expr *visitCopyExpr(CopyExpr *expr) {
3529+
auto toType = simplifyType(cs.getType(expr));
3530+
cs.setType(expr, toType);
3531+
3532+
auto *subExpr = expr->getSubExpr();
3533+
auto type = simplifyType(cs.getType(subExpr));
3534+
3535+
// Let's load the value associated with this try.
3536+
if (type->hasLValueType()) {
3537+
subExpr = coerceToType(subExpr, type->getRValueType(),
3538+
cs.getConstraintLocator(subExpr));
3539+
3540+
if (!subExpr)
3541+
return nullptr;
3542+
}
3543+
3544+
expr->setSubExpr(subExpr);
3545+
3546+
return expr;
3547+
}
3548+
35283549
Expr *visitAnyTryExpr(AnyTryExpr *expr) {
35293550
auto *subExpr = expr->getSubExpr();
35303551
auto type = simplifyType(cs.getType(subExpr));

lib/Sema/CSGen.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1858,6 +1858,16 @@ namespace {
18581858
return CS.getType(expr->getSubExpr());
18591859
}
18601860

1861+
Type visitCopyExpr(CopyExpr *expr) {
1862+
auto valueTy = CS.createTypeVariable(CS.getConstraintLocator(expr),
1863+
TVO_PrefersSubtypeBinding |
1864+
TVO_CanBindToNoEscape);
1865+
CS.addConstraint(ConstraintKind::Equal, valueTy,
1866+
CS.getType(expr->getSubExpr()),
1867+
CS.getConstraintLocator(expr));
1868+
return valueTy;
1869+
}
1870+
18611871
Type visitAnyTryExpr(AnyTryExpr *expr) {
18621872
return CS.getType(expr->getSubExpr());
18631873
}

lib/Sema/MiscDiagnostics.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,12 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
344344
checkMoveExpr(moveExpr);
345345
}
346346

347+
// Diagnose copy expression uses where the sub expression is not a declref
348+
// expr.
349+
if (auto *copyExpr = dyn_cast<CopyExpr>(E)) {
350+
checkCopyExpr(copyExpr);
351+
}
352+
347353
// Diagnose move expression uses where the sub expression is not a declref expr
348354
if (auto *borrowExpr = dyn_cast<BorrowExpr>(E)) {
349355
checkBorrowExpr(borrowExpr);
@@ -422,6 +428,26 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
422428
}
423429
}
424430

431+
void checkCopyExpr(CopyExpr *copyExpr) {
432+
// Do not allow for copy_expr to be used with pure move only types. We
433+
// /do/ allow it to be used with no implicit copy types though.
434+
if (copyExpr->getType()->isPureMoveOnly()) {
435+
Ctx.Diags.diagnose(
436+
copyExpr->getLoc(),
437+
diag::copy_expression_cannot_be_used_with_noncopyable_types);
438+
}
439+
440+
// We only allow for copy_expr to be applied directly to lvalues. We do
441+
// not allow currently for it to be applied to fields.
442+
auto *subExpr = copyExpr->getSubExpr();
443+
if (auto *li = dyn_cast<LoadExpr>(subExpr))
444+
subExpr = li->getSubExpr();
445+
if (!isa<DeclRefExpr>(subExpr)) {
446+
Ctx.Diags.diagnose(copyExpr->getLoc(),
447+
diag::copy_expression_not_passed_lvalue);
448+
}
449+
}
450+
425451
void checkBorrowExpr(BorrowExpr *borrowExpr) {
426452
// Allow for a chain of member_ref exprs that end in a decl_ref expr.
427453
auto *subExpr = borrowExpr->getSubExpr();

test/IDE/copy_expr.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t
3+
4+
func test(myParam: Int) {
5+
copy #^COPY^#
6+
// COPY: Decl[LocalVar]/Local: myParam[#Int#]; name=myParam
7+
}

0 commit comments

Comments
 (0)