Skip to content

Commit 4cd1201

Browse files
committed
[noImplicitCopy] Add support for marking a SILArgument as being a NoImplicitCopy argument.
I am purposely doing this in SILGen rather than at the type system level to avoid having to have to add a bunch of boilerplate to the type system. Instead of doing that, I am in SILGen checking for the isNoImplicitCopy bit on the ParamDecl when we emit arguments. At that point, I set on the specific SILArgument being emitted the bit that it is no implicit copy. In terms of printing at the SIL level, I just printed it in front of the function argument type like @owned, e.x.: func myFunc(_ x: @_noImplicitCopy T) -> T { ... } becomes: bb0(%0 : @noImplicitCopy @owned $T): Some notes: * Just to be explicit, I am making it so that no implicit copy parameters by default are always passed at +1. The reason why I think this makes sense is that this is the natural way of working with a move only value. * As always, one can not write no implicit copy the attribute without passing the flag -enable-experimental-move-only so this is NFC. rdar://83957088
1 parent 3290833 commit 4cd1201

17 files changed

+240
-49
lines changed

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ SIMPLE_DECL_ATTR(_noImplicitCopy, NoImplicitCopy,
676676
UserInaccessible |
677677
ABIStableToAdd | ABIBreakingToRemove |
678678
APIStableToAdd | APIBreakingToRemove |
679-
OnVar,
679+
OnParam | OnVar,
680680
122)
681681

682682
SIMPLE_DECL_ATTR(_noLocks, NoLocks,

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5371,6 +5371,10 @@ class ParamDecl : public VarDecl {
53715371
Bits.ParamDecl.defaultArgumentKind = static_cast<unsigned>(K);
53725372
}
53735373

5374+
bool isNoImplicitCopy() const {
5375+
return getAttrs().hasAttribute<NoImplicitCopyAttr>();
5376+
}
5377+
53745378
/// Whether this parameter has a default argument expression available.
53755379
///
53765380
/// Note that this will return false for deserialized declarations, which only

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6040,8 +6040,8 @@ ERROR(experimental_moveonly_feature_can_only_be_used_when_enabled,
60406040
none, "Can not use feature when experimental move only is disabled! Pass"
60416041
" the frontend flag -enable-experimental-move-only to swift to enable "
60426042
"the usage of this language feature", ())
6043-
ERROR(noimplicitcopy_attr_valid_only_on_local_let,
6044-
none, "'@_noImplicitCopy' attribute can only be applied to local lets", ())
6043+
ERROR(noimplicitcopy_attr_valid_only_on_local_let_params,
6044+
none, "'@_noImplicitCopy' attribute can only be applied to local lets and params", ())
60456045
ERROR(noimplicitcopy_attr_invalid_in_generic_context,
60466046
none, "'@_noImplicitCopy' attribute cannot be applied to entities in generic contexts", ())
60476047

include/swift/SIL/SILArgument.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ class SILArgument : public ValueBase {
111111
node->getKind() <= SILNodeKind::Last_SILArgument;
112112
}
113113

114+
bool isNoImplicitCopy() const;
115+
114116
unsigned getIndex() const;
115117

116118
/// Return true if this block argument is actually a phi argument as
@@ -311,11 +313,15 @@ class SILPhiArgument : public SILArgument {
311313
class SILFunctionArgument : public SILArgument {
312314
friend class SILBasicBlock;
313315

316+
bool noImplicitCopy = false;
317+
314318
SILFunctionArgument(SILBasicBlock *parentBlock, SILType type,
315319
ValueOwnershipKind ownershipKind,
316-
const ValueDecl *decl = nullptr)
320+
const ValueDecl *decl = nullptr,
321+
bool isNoImplicitCopy = false)
317322
: SILArgument(ValueKind::SILFunctionArgument, parentBlock, type,
318-
ownershipKind, decl) {}
323+
ownershipKind, decl),
324+
noImplicitCopy(isNoImplicitCopy) {}
319325
// A special constructor, only intended for use in
320326
// SILBasicBlock::replaceFunctionArg.
321327
explicit SILFunctionArgument(SILType type, ValueOwnershipKind ownershipKind,
@@ -324,6 +330,10 @@ class SILFunctionArgument : public SILArgument {
324330
}
325331

326332
public:
333+
bool isNoImplicitCopy() const { return noImplicitCopy; }
334+
335+
void setNoImplicitCopy(bool newValue) { noImplicitCopy = newValue; }
336+
327337
bool isIndirectResult() const;
328338

329339
SILArgumentConvention getArgumentConvention() const;
@@ -365,6 +375,12 @@ inline bool SILArgument::isPhiArgument() const {
365375
llvm_unreachable("Covered switch is not covered?!");
366376
}
367377

378+
inline bool SILArgument::isNoImplicitCopy() const {
379+
if (auto *fArg = dyn_cast<SILFunctionArgument>(this))
380+
return fArg->isNoImplicitCopy();
381+
return false;
382+
}
383+
368384
inline bool SILArgument::isTerminatorResult() const {
369385
switch (getKind()) {
370386
case SILArgumentKind::SILPhiArgument:

lib/SIL/IR/SILPrinter.cpp

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,21 @@ struct SILValuePrinterInfo {
155155
ID ValueID;
156156
SILType Type;
157157
Optional<ValueOwnershipKind> OwnershipKind;
158+
bool IsNoImplicitCopy = false;
158159

159160
SILValuePrinterInfo(ID ValueID) : ValueID(ValueID), Type(), OwnershipKind() {}
160161
SILValuePrinterInfo(ID ValueID, SILType Type)
161162
: ValueID(ValueID), Type(Type), OwnershipKind() {}
162163
SILValuePrinterInfo(ID ValueID, SILType Type,
163164
ValueOwnershipKind OwnershipKind)
164165
: ValueID(ValueID), Type(Type), OwnershipKind(OwnershipKind) {}
166+
SILValuePrinterInfo(ID ValueID, SILType Type,
167+
ValueOwnershipKind OwnershipKind, bool IsNoImplicitCopy)
168+
: ValueID(ValueID), Type(Type), OwnershipKind(OwnershipKind),
169+
IsNoImplicitCopy(IsNoImplicitCopy) {}
170+
SILValuePrinterInfo(ID ValueID, SILType Type, bool IsNoImplicitCopy)
171+
: ValueID(ValueID), Type(Type), OwnershipKind(),
172+
IsNoImplicitCopy(IsNoImplicitCopy) {}
165173
};
166174

167175
/// Return the fully qualified dotted path for DeclContext.
@@ -623,6 +631,8 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
623631
if (!i.Type)
624632
return *this;
625633
*this << " : ";
634+
if (i.IsNoImplicitCopy)
635+
*this << "@noImplicitCopy ";
626636
if (i.OwnershipKind && *i.OwnershipKind != OwnershipKind::None) {
627637
*this << "@" << i.OwnershipKind.getValue() << " ";
628638
}
@@ -655,9 +665,16 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
655665
SILValuePrinterInfo getIDAndType(SILValue V) {
656666
return {Ctx.getID(V), V ? V->getType() : SILType()};
657667
}
668+
SILValuePrinterInfo getIDAndType(SILFunctionArgument *arg) {
669+
return {Ctx.getID(arg), arg->getType(), arg->isNoImplicitCopy()};
670+
}
658671
SILValuePrinterInfo getIDAndTypeAndOwnership(SILValue V) {
659672
return {Ctx.getID(V), V ? V->getType() : SILType(), V.getOwnershipKind()};
660673
}
674+
SILValuePrinterInfo getIDAndTypeAndOwnership(SILFunctionArgument *arg) {
675+
return {Ctx.getID(arg), arg->getType(), arg->getOwnershipKind(),
676+
arg->isNoImplicitCopy()};
677+
}
661678

662679
//===--------------------------------------------------------------------===//
663680
// Big entrypoints.
@@ -728,20 +745,40 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
728745
if (BB->args_empty())
729746
return;
730747
*this << '(';
731-
ArrayRef<SILArgument *> Args = BB->getArguments();
732-
733748
// If SIL ownership is enabled and the given function has not had ownership
734749
// stripped out, print out ownership of SILArguments.
735750
if (BB->getParent()->hasOwnership()) {
736-
*this << getIDAndTypeAndOwnership(Args[0]);
737-
for (SILArgument *Arg : Args.drop_front()) {
738-
*this << ", " << getIDAndTypeAndOwnership(Arg);
751+
if (BB->isEntry()) {
752+
auto Args = BB->getSILFunctionArguments();
753+
*this << getIDAndTypeAndOwnership(Args[0]);
754+
for (unsigned i : range(1, Args.size())) {
755+
SILFunctionArgument *Arg = Args[i];
756+
*this << ", " << getIDAndTypeAndOwnership(Arg);
757+
}
758+
*this << ')';
759+
} else {
760+
ArrayRef<SILArgument *> Args = BB->getArguments();
761+
*this << getIDAndTypeAndOwnership(Args[0]);
762+
for (SILArgument *Arg : Args.drop_front()) {
763+
*this << ", " << getIDAndTypeAndOwnership(Arg);
764+
}
765+
*this << ')';
766+
}
767+
return;
768+
}
769+
770+
if (BB->isEntry()) {
771+
auto Args = BB->getSILFunctionArguments();
772+
*this << getIDAndType(Args[0]);
773+
for (unsigned i : range(1, Args.size())) {
774+
SILFunctionArgument *Arg = Args[i];
775+
*this << ", " << getIDAndType(Arg);
739776
}
740777
*this << ')';
741778
return;
742779
}
743780

744-
// Otherwise, fall back to the old behavior
781+
ArrayRef<SILArgument *> Args = BB->getArguments();
745782
*this << getIDAndType(Args[0]);
746783
for (SILArgument *Arg : Args.drop_front()) {
747784
*this << ", " << getIDAndType(Arg);

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,22 @@ namespace {
294294
TypeLoc = P.Tok.getLoc();
295295
return parseASTType(result, genericEnv, genericParams);
296296
}
297+
298+
bool parseIsNoImplicitCopy() {
299+
// We parse here @ <identifier>.
300+
if (P.Tok.getKind() != tok::at_sign)
301+
return false;
302+
303+
// Make sure our text is no implicit copy.
304+
if (P.peekToken().getText() != "noImplicitCopy")
305+
return false;
306+
307+
// Ok, we can do this.
308+
P.consumeToken(tok::at_sign);
309+
P.consumeToken(tok::identifier);
310+
return true;
311+
}
312+
297313
bool parseSILOwnership(ValueOwnershipKind &OwnershipKind) {
298314
// We parse here @ <identifier>.
299315
if (!P.consumeIf(tok::at_sign)) {
@@ -6108,6 +6124,8 @@ bool SILParser::parseSILBasicBlock(SILBuilder &B) {
61086124
P.parseToken(tok::colon, diag::expected_sil_colon_value_ref))
61096125
return true;
61106126

6127+
bool foundNoImplicitCopy = parseIsNoImplicitCopy();
6128+
61116129
// If SILOwnership is enabled and we are not assuming that we are
61126130
// parsing unqualified SIL, look for printed value ownership kinds.
61136131
if (F->hasOwnership() &&
@@ -6119,7 +6137,10 @@ bool SILParser::parseSILBasicBlock(SILBuilder &B) {
61196137

61206138
SILArgument *Arg;
61216139
if (IsEntry) {
6122-
Arg = BB->createFunctionArgument(Ty);
6140+
auto *fArg = BB->createFunctionArgument(Ty);
6141+
fArg->setNoImplicitCopy(foundNoImplicitCopy);
6142+
Arg = fArg;
6143+
61236144
// Today, we construct the ownership kind straight from the function
61246145
// type. Make sure they are in sync, otherwise bail. We want this to
61256146
// be an exact check rather than a compatibility check since we do not

lib/SILGen/SILGenBuilder.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -432,12 +432,14 @@ ManagedValue SILGenBuilder::createLoadCopy(SILLocation loc, ManagedValue v,
432432

433433
static ManagedValue createInputFunctionArgument(SILGenBuilder &B, SILType type,
434434
SILLocation loc,
435-
ValueDecl *decl = nullptr) {
435+
ValueDecl *decl = nullptr,
436+
bool isNoImplicitCopy = false) {
436437
auto &SGF = B.getSILGenFunction();
437438
SILFunction &F = B.getFunction();
438439
assert((F.isBare() || decl) &&
439440
"Function arguments of non-bare functions must have a decl");
440441
auto *arg = F.begin()->createFunctionArgument(type, decl);
442+
arg->setNoImplicitCopy(isNoImplicitCopy);
441443
switch (arg->getArgumentConvention()) {
442444
case SILArgumentConvention::Indirect_In_Guaranteed:
443445
case SILArgumentConvention::Direct_Guaranteed:
@@ -469,8 +471,10 @@ static ManagedValue createInputFunctionArgument(SILGenBuilder &B, SILType type,
469471
}
470472

471473
ManagedValue SILGenBuilder::createInputFunctionArgument(SILType type,
472-
ValueDecl *decl) {
473-
return ::createInputFunctionArgument(*this, type, SILLocation(decl), decl);
474+
ValueDecl *decl,
475+
bool isNoImplicitCopy) {
476+
return ::createInputFunctionArgument(*this, type, SILLocation(decl), decl,
477+
isNoImplicitCopy);
474478
}
475479

476480
ManagedValue

lib/SILGen/SILGenBuilder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,8 @@ class SILGenBuilder : public SILBuilder {
223223

224224
/// Create a SILArgument for an input parameter. Asserts if used to create a
225225
/// function argument for an out parameter.
226-
ManagedValue createInputFunctionArgument(SILType type, ValueDecl *decl);
226+
ManagedValue createInputFunctionArgument(SILType type, ValueDecl *decl,
227+
bool isNoImplicitCopy = false);
227228

228229
/// Create a SILArgument for an input parameter. Uses \p loc to create any
229230
/// copies necessary. Asserts if used to create a function argument for an out

lib/SILGen/SILGenProlog.cpp

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,28 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "SILGenFunction.h"
13+
#include "ArgumentSource.h"
1414
#include "ExecutorBreadcrumb.h"
1515
#include "Initialization.h"
1616
#include "ManagedValue.h"
17+
#include "SILGenFunction.h"
1718
#include "Scope.h"
18-
#include "ArgumentSource.h"
19-
#include "swift/SIL/SILArgument.h"
2019
#include "swift/AST/CanTypeVisitor.h"
20+
#include "swift/AST/DiagnosticsSIL.h"
2121
#include "swift/AST/GenericEnvironment.h"
2222
#include "swift/AST/ParameterList.h"
2323
#include "swift/AST/PropertyWrappers.h"
24+
#include "swift/SIL/SILArgument.h"
2425

2526
using namespace swift;
2627
using namespace Lowering;
2728

29+
template <typename... T, typename... U>
30+
static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
31+
U &&...args) {
32+
Context.Diags.diagnose(loc, diag, std::forward<U>(args)...);
33+
}
34+
2835
SILValue SILGenFunction::emitSelfDecl(VarDecl *selfDecl) {
2936
// Emit the implicit 'self' argument.
3037
SILType selfType = getLoweredLoadableType(selfDecl->getType());
@@ -49,11 +56,13 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
4956
SILLocation loc;
5057
CanSILFunctionType fnTy;
5158
ArrayRef<SILParameterInfo> &parameters;
59+
bool isNoImplicitCopy;
5260

5361
EmitBBArguments(SILGenFunction &sgf, SILBasicBlock *parent, SILLocation l,
5462
CanSILFunctionType fnTy,
55-
ArrayRef<SILParameterInfo> &parameters)
56-
: SGF(sgf), parent(parent), loc(l), fnTy(fnTy), parameters(parameters) {}
63+
ArrayRef<SILParameterInfo> &parameters, bool isNoImplicitCopy)
64+
: SGF(sgf), parent(parent), loc(l), fnTy(fnTy), parameters(parameters),
65+
isNoImplicitCopy(isNoImplicitCopy) {}
5766

5867
ManagedValue visitType(CanType t, AbstractionPattern orig) {
5968
return visitType(t, orig, /*isInOut=*/false);
@@ -79,7 +88,7 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
7988
auto paramType =
8089
SGF.F.mapTypeIntoContext(SGF.getSILType(parameterInfo, fnTy));
8190
ManagedValue mv = SGF.B.createInputFunctionArgument(
82-
paramType, loc.getAsASTNode<ValueDecl>());
91+
paramType, loc.getAsASTNode<ValueDecl>(), isNoImplicitCopy);
8392

8493
// This is a hack to deal with the fact that Self.Type comes in as a static
8594
// metatype, but we have to downcast it to a dynamic Self metatype to get
@@ -98,12 +107,21 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
98107

99108
// This can happen if the value is resilient in the calling convention
100109
// but not resilient locally.
101-
if (argType.isLoadable(SGF.F) && argType.isAddress()) {
102-
if (mv.isPlusOne(SGF))
103-
mv = SGF.B.createLoadTake(loc, mv);
104-
else
105-
mv = SGF.B.createLoadBorrow(loc, mv);
106-
argType = argType.getObjectType();
110+
if (argType.isLoadable(SGF.F)) {
111+
if (argType.isAddress()) {
112+
if (mv.isPlusOne(SGF))
113+
mv = SGF.B.createLoadTake(loc, mv);
114+
else
115+
mv = SGF.B.createLoadBorrow(loc, mv);
116+
argType = argType.getObjectType();
117+
}
118+
} else {
119+
if (isNoImplicitCopy) {
120+
// We do not support no implicit copy address only types. Emit an error.
121+
auto diag = diag::noimplicitcopy_used_on_generic_or_existential;
122+
diagnose(SGF.getASTContext(), mv.getValue().getLoc().getSourceLoc(),
123+
diag);
124+
}
107125
}
108126

109127
if (argType.getASTType() != paramType.getASTType()) {
@@ -217,14 +235,14 @@ struct ArgumentInitHelper {
217235

218236
unsigned getNumArgs() const { return ArgNo; }
219237

220-
ManagedValue makeArgument(Type ty, bool isInOut, SILBasicBlock *parent,
221-
SILLocation l) {
238+
ManagedValue makeArgument(Type ty, bool isInOut, bool isNoImplicitCopy,
239+
SILBasicBlock *parent, SILLocation l) {
222240
assert(ty && "no type?!");
223241

224242
// Create an RValue by emitting destructured arguments into a basic block.
225243
CanType canTy = ty->getCanonicalType();
226-
EmitBBArguments argEmitter(SGF, parent, l,
227-
f.getLoweredFunctionType(), parameters);
244+
EmitBBArguments argEmitter(SGF, parent, l, f.getLoweredFunctionType(),
245+
parameters, isNoImplicitCopy);
228246

229247
// Note: inouts of tuples are not exploded, so we bypass visit().
230248
AbstractionPattern origTy = OrigFnType
@@ -241,7 +259,8 @@ struct ArgumentInitHelper {
241259
SILLocation loc(pd);
242260
loc.markAsPrologue();
243261

244-
ManagedValue argrv = makeArgument(ty, pd->isInOut(), parent, loc);
262+
ManagedValue argrv =
263+
makeArgument(ty, pd->isInOut(), pd->isNoImplicitCopy(), parent, loc);
245264

246265
if (pd->isInOut()) {
247266
assert(argrv.getType().isAddress() && "expected inout to be address");
@@ -297,7 +316,8 @@ struct ArgumentInitHelper {
297316
Scope discardScope(SGF.Cleanups, CleanupLocation(PD));
298317

299318
// Manage the parameter.
300-
auto argrv = makeArgument(type, PD->isInOut(), &*f.begin(), paramLoc);
319+
auto argrv = makeArgument(type, PD->isInOut(), PD->isNoImplicitCopy(),
320+
&*f.begin(), paramLoc);
301321

302322
// Emit debug information for the argument.
303323
SILLocation loc(PD);

0 commit comments

Comments
 (0)