Skip to content

Commit d14e9eb

Browse files
committed
[copy-operator] Add support for the copy operator in preparation for making consuming and borrowing no implicit copy.
rdar://101862423
1 parent f16bf16 commit d14e9eb

17 files changed

+878
-0
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+
///
2083+
/// getSemanticsProvidingExpr() looks through this because it doesn't
2084+
/// provide the value and only very specific clients care where the
2085+
/// 'move' was written.
2086+
class CopyExpr final : public IdentityExpr {
2087+
SourceLoc CopyLoc;
2088+
2089+
public:
2090+
CopyExpr(SourceLoc copyLoc, Expr *sub, Type type = Type(),
2091+
bool implicit = false)
2092+
: IdentityExpr(ExprKind::Copy, sub, type, implicit), CopyLoc(copyLoc) {}
2093+
2094+
static CopyExpr *createImplicit(ASTContext &ctx, SourceLoc copyLoc, Expr *sub,
2095+
Type type = Type()) {
2096+
return new (ctx) CopyExpr(copyLoc, sub, type, /*implicit=*/true);
2097+
}
2098+
2099+
SourceLoc getLoc() const { return CopyLoc; }
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
@@ -108,6 +108,7 @@ ABSTRACT_EXPR(Identity, Expr)
108108
EXPR(DotSelf, IdentityExpr)
109109
EXPR(Await, IdentityExpr)
110110
EXPR(Move, IdentityExpr)
111+
EXPR(Copy, IdentityExpr)
111112
EXPR(Borrow, IdentityExpr)
112113
EXPR(UnresolvedMemberChainResult, IdentityExpr)
113114
EXPR_RANGE(Identity, Paren, UnresolvedMemberChainResult)

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/Expr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const {
372372
PASS_THROUGH_REFERENCE(DotSelf, getSubExpr);
373373
PASS_THROUGH_REFERENCE(Await, getSubExpr);
374374
PASS_THROUGH_REFERENCE(Move, getSubExpr);
375+
PASS_THROUGH_REFERENCE(Copy, getSubExpr);
375376
PASS_THROUGH_REFERENCE(Borrow, getSubExpr);
376377
PASS_THROUGH_REFERENCE(Try, getSubExpr);
377378
PASS_THROUGH_REFERENCE(ForceTry, getSubExpr);
@@ -744,6 +745,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const {
744745

745746
case ExprKind::Await:
746747
case ExprKind::Move:
748+
case ExprKind::Copy:
747749
case ExprKind::Borrow:
748750
case ExprKind::Try:
749751
case ExprKind::ForceTry:
@@ -936,6 +938,7 @@ bool Expr::isValidParentOfTypeExpr(Expr *typeExpr) const {
936938
case ExprKind::Paren:
937939
case ExprKind::Await:
938940
case ExprKind::Move:
941+
case ExprKind::Copy:
939942
case ExprKind::Borrow:
940943
case ExprKind::UnresolvedMemberChainResult:
941944
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,
427+
tok::dollarident) &&
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.hasCodeCompletion() && !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: 33 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,38 @@ 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 = cast<DeclRefExpr>(E->getSubExpr());
6162+
auto subASTType = subExpr->getType()->getCanonicalType();
6163+
6164+
auto subType = SGF.getLoweredType(subASTType);
6165+
6166+
if (subType.isLoadable(SGF.F)) {
6167+
ManagedValue mv = SGF.emitRValue(subExpr).getAsSingleValue(SGF, subExpr);
6168+
if (mv.getType().isTrivial(SGF.F))
6169+
return RValue(SGF, {mv}, subType.getASTType());
6170+
mv = SGF.B.createExplicitCopyValue(E, mv);
6171+
return RValue(SGF, {mv}, subType.getASTType());
6172+
}
6173+
6174+
// If we aren't loadable, then create a temporary initialization and
6175+
// explicit_copy_addr into that.
6176+
std::unique_ptr<TemporaryInitialization> optTemp;
6177+
optTemp = SGF.emitTemporary(E, SGF.getTypeLowering(subType));
6178+
SILValue toAddr = optTemp->getAddressForInPlaceInitialization(SGF, E);
6179+
assert(!isa<LValueType>(E->getType()->getCanonicalType()) &&
6180+
"Shouldn't see an lvalue type here");
6181+
6182+
ManagedValue mv =
6183+
SGF.emitRValue(subExpr, SGFContext(SGFContext::AllowImmediatePlusZero))
6184+
.getAsSingleValue(SGF, subExpr);
6185+
assert(mv.getType().isAddress());
6186+
SGF.B.createExplicitCopyAddr(subExpr, mv.getValue(), toAddr, IsNotTake,
6187+
IsInitialization);
6188+
optTemp->finishInitialization(SGF);
6189+
return RValue(SGF, {optTemp->getManagedAddress()}, subType.getASTType());
6190+
}
6191+
61596192
RValue RValueEmitter::visitMacroExpansionExpr(MacroExpansionExpr *E,
61606193
SGFContext C) {
61616194
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/MiscDiagnostics.cpp

Lines changed: 23 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,23 @@ 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+
if (!isa<DeclRefExpr>(copyExpr->getSubExpr())) {
443+
Ctx.Diags.diagnose(copyExpr->getLoc(),
444+
diag::copy_expression_not_passed_lvalue);
445+
}
446+
}
447+
425448
void checkBorrowExpr(BorrowExpr *borrowExpr) {
426449
// Allow for a chain of member_ref exprs that end in a decl_ref expr.
427450
auto *subExpr = borrowExpr->getSubExpr();

test/Parse/copy_expr.swift

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking
2+
3+
var global: Int = 5
4+
func testGlobal() {
5+
let _ = copy global
6+
}
7+
8+
func testLet() {
9+
let t = String()
10+
let _ = copy t
11+
}
12+
13+
func testVar() {
14+
var t = String()
15+
t = String()
16+
let _ = copy t
17+
}
18+
19+
func copy() {}
20+
func copy(_: String) {}
21+
func copy(_: String, _: Int) {}
22+
func copy(x: String, y: Int) {}
23+
24+
// Ensure that we can still call a function named copy.
25+
26+
func useCopyFunc() {
27+
var s = String()
28+
var i = global
29+
30+
copy()
31+
copy(s)
32+
copy(i) // expected-error{{cannot convert value of type 'Int' to expected argument type 'String'}}
33+
copy(s, i)
34+
copy(i, s) // expected-error{{unnamed argument #2 must precede unnamed argument #1}}
35+
copy(x: s, y: i)
36+
copy(y: i, x: s) // expected-error{{argument 'x' must precede argument 'y'}}
37+
}
38+
39+
// Ensure that we can still use a variable named copy.
40+
41+
func useCopyVar(copy: inout String) {
42+
let s = copy
43+
copy = s
44+
45+
// We can copy from a variable named `copy`
46+
let t = copy copy
47+
copy = t
48+
49+
// We can do member access and subscript a variable named `copy`
50+
let i = copy.startIndex
51+
let _ = copy[i]
52+
}
53+
54+
@propertyWrapper
55+
struct FooWrapper<T> {
56+
var value: T
57+
58+
init(wrappedValue: T) { value = wrappedValue }
59+
60+
var wrappedValue: T {
61+
get { value }
62+
nonmutating set {}
63+
}
64+
var projectedValue: T {
65+
get { value }
66+
nonmutating set {}
67+
}
68+
}
69+
70+
struct Foo {
71+
@FooWrapper var wrapperTest: String
72+
73+
func copySelf() {
74+
_ = copy self
75+
}
76+
77+
func copyPropertyWrapper() {
78+
// Make sure that we can parse.
79+
_ = copy wrapperTest // expected-error {{'copy' can only be applied to lvalues}}
80+
_ = copy _wrapperTest // expected-error {{'copy' can only be applied to lvalues}}
81+
_ = copy $wrapperTest // expected-error {{'copy' can only be applied to lvalues}}
82+
}
83+
}
84+
85+
func testParseCopyWithDollarIdentifier() {
86+
class Klass {}
87+
let f: (Klass) -> () = {
88+
let _ = copy $0
89+
}
90+
_ = f
91+
}
92+
93+
func testParseCopySelf() {
94+
class Klass {
95+
func test() {
96+
let _ = copy self
97+
}
98+
}
99+
}
100+
101+
func testForLoop() {
102+
for copy in 0..<1024 {
103+
_ = copy
104+
}
105+
}

0 commit comments

Comments
 (0)