Skip to content

Commit 8aef84c

Browse files
authored
Merge pull request swiftlang#39927 from gottesmm/pr-7b7eae43b3c94f52f927ce574f94861e33da7f4a
[moveOnly] Add an @_noImplicitCopy decl attribute and allow it to be only attached to local lets.
2 parents 6e11611 + a589b4a commit 8aef84c

File tree

8 files changed

+229
-1
lines changed

8 files changed

+229
-1
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,3 +620,8 @@ within Swift 5 code that has adopted concurrency, but non-`@MainActor`
620620

621621
See the forum post on [Concurrency in Swift 5 and 6](https://forums.swift.org/t/concurrency-in-swift-5-and-6/49337)
622622
for more details.
623+
624+
## `@_noImplicitCopy`
625+
626+
Marks a var decl as a variable that must be copied explicitly using the builtin
627+
function Builtin.copy.

include/swift/AST/Attr.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,13 @@ DECL_ATTR(_nonSendable, NonSendable,
672672
APIStableToAdd | APIBreakingToRemove,
673673
121)
674674

675+
SIMPLE_DECL_ATTR(_noImplicitCopy, NoImplicitCopy,
676+
UserInaccessible |
677+
ABIStableToAdd | ABIBreakingToRemove |
678+
APIStableToAdd | APIBreakingToRemove |
679+
OnVar,
680+
122)
681+
675682
// If you're adding a new underscored attribute here, please document it in
676683
// docs/ReferenceGuides/UnderscoredAttributes.md.
677684

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6029,5 +6029,12 @@ ERROR(wrap_invalid_attr_added_by_access_note, none,
60296029

60306030
#undef WHICH_ACCESS_NOTE
60316031

6032+
// Move Only diagnostics
6033+
6034+
ERROR(noimplicitcopy_attr_valid_only_on_local_let,
6035+
none, "'@_noImplicitCopy' attribute can only be applied to local lets", ())
6036+
ERROR(noimplicitcopy_attr_invalid_in_generic_context,
6037+
none, "'@_noImplicitCopy' attribute cannot be applied to entities in generic contexts", ())
6038+
60326039
#define UNDEFINE_DIAGNOSTIC_MACROS
60336040
#include "DefineDiagnosticMacros.h"

lib/Sema/TypeCheckAttr.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "TypeChecker.h"
2323
#include "swift/AST/ASTVisitor.h"
2424
#include "swift/AST/ClangModuleLoader.h"
25+
#include "swift/AST/Decl.h"
2526
#include "swift/AST/DiagnosticsParse.h"
2627
#include "swift/AST/Effects.h"
2728
#include "swift/AST/GenericEnvironment.h"
@@ -262,10 +263,45 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
262263

263264
void visitReasyncAttr(ReasyncAttr *attr);
264265
void visitNonisolatedAttr(NonisolatedAttr *attr);
266+
267+
void visitNoImplicitCopyAttr(NoImplicitCopyAttr *attr);
265268
};
266269

267270
} // end anonymous namespace
268271

272+
void AttributeChecker::visitNoImplicitCopyAttr(NoImplicitCopyAttr *attr) {
273+
auto *dc = D->getDeclContext();
274+
auto *vd = dyn_cast<VarDecl>(D);
275+
if (!vd) {
276+
auto error = diag::noimplicitcopy_attr_valid_only_on_local_let;
277+
diagnoseAndRemoveAttr(attr, error);
278+
return;
279+
}
280+
281+
// If we have a 'var' instead of a 'let', bail. We only support on local lets.
282+
if (!vd->isLet()) {
283+
auto error = diag::noimplicitcopy_attr_valid_only_on_local_let;
284+
diagnoseAndRemoveAttr(attr, error);
285+
return;
286+
}
287+
288+
if (vd->hasStorage()) {
289+
// We do not support fields of nominal types now.
290+
if (isa<NominalTypeDecl>(dc)) {
291+
auto error = diag::noimplicitcopy_attr_valid_only_on_local_let;
292+
diagnoseAndRemoveAttr(attr, error);
293+
return;
294+
}
295+
}
296+
297+
// We do not support static or global vars either yet.
298+
if (dc->isModuleScopeContext() || (dc->isTypeContext() && vd->isStatic())) {
299+
auto error = diag::noimplicitcopy_attr_valid_only_on_local_let;
300+
diagnoseAndRemoveAttr(attr, error);
301+
return;
302+
}
303+
}
304+
269305
void AttributeChecker::visitTransparentAttr(TransparentAttr *attr) {
270306
DeclContext *dc = D->getDeclContext();
271307
// Protocol declarations cannot be transparent.

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,7 @@ namespace {
15571557
UNINTERESTING_ATTR(ImplicitSelfCapture)
15581558
UNINTERESTING_ATTR(InheritActorContext)
15591559
UNINTERESTING_ATTR(Isolated)
1560+
UNINTERESTING_ATTR(NoImplicitCopy)
15601561
#undef UNINTERESTING_ATTR
15611562

15621563
void visitAvailableAttr(AvailableAttr *attr) {

lib/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5656
/// describe what change you made. The content of this comment isn't important;
5757
/// it just ensures a conflict if two people change the module format.
5858
/// Don't worry about adhering to the 80-column limit for this line.
59-
const uint16_t SWIFTMODULE_VERSION_MINOR = 636; // 'isolated' in param decls
59+
const uint16_t SWIFTMODULE_VERSION_MINOR = 637; // @_noImplicitCopy
6060

6161
/// A standard hash seed used for all string hashes in a serialized module.
6262
///

test/Parse/noimplicitcopy_attr.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
f// RUN: %target-typecheck-verify-swift -parse -parse-stdlib -disable-availability-checking -verify-syntax-tree
2+
3+
import Swift
4+
5+
class Klass {}
6+
7+
func argumentsAndReturns(@_noImplicitCopy _ x: Klass) -> Klass {
8+
return x
9+
}
10+
11+
func letDecls(@_noImplicitCopy _ x: Klass) -> () {
12+
@_noImplicitCopy let y: Klass = x
13+
print(y)
14+
}
15+
16+
func varDecls(@_noImplicitCopy _ x: Klass, @_noImplicitCopy _ x2: Klass) -> () {
17+
@_noImplicitCopy var y: Klass = x
18+
y = x2
19+
print(y)
20+
}
21+
22+
func getKlass() -> Builtin.NativeObject {
23+
let k = Klass()
24+
let b = Builtin.unsafeCastToNativeObject(k)
25+
return Builtin.move(b)
26+
}
27+
28+
@_noImplicitCopy var g: Builtin.NativeObject = getKlass()
29+
@_noImplicitCopy let g2: Builtin.NativeObject = getKlass()
30+
31+

test/Sema/noimplicitcopy_attr.swift

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// RUN: %target-typecheck-verify-swift -parse-stdlib -disable-availability-checking -verify-syntax-tree
2+
3+
import Swift
4+
5+
class Klass {}
6+
7+
func argumentsAndReturns(@_noImplicitCopy _ x: Klass) -> Klass { // expected-error {{@_noImplicitCopy may only be used on 'var' declarations}}
8+
return x
9+
}
10+
func letDecls(_ x: Klass) -> () {
11+
@_noImplicitCopy let y: Klass = x
12+
print(y)
13+
}
14+
15+
func varDecls(_ x: Klass, _ x2: Klass) -> () {
16+
@_noImplicitCopy var y: Klass = x // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
17+
y = x2
18+
print(y)
19+
}
20+
21+
func getKlass() -> Builtin.NativeObject {
22+
let k = Klass()
23+
let b = Builtin.unsafeCastToNativeObject(k)
24+
return Builtin.move(b)
25+
}
26+
27+
@_noImplicitCopy var g: Builtin.NativeObject = getKlass() // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
28+
@_noImplicitCopy let g2: Builtin.NativeObject = getKlass() // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
29+
@_noImplicitCopy var g3: Builtin.NativeObject { getKlass() } // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
30+
31+
struct MyStruct {
32+
// Error if @_noImplicitCopy on struct fields. We do not have move only types and
33+
// these are part of MyStruct.
34+
//
35+
// TODO: Make error specific for move only on struct/enum.
36+
@_noImplicitCopy var x: Builtin.NativeObject = getKlass() // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
37+
@_noImplicitCopy let y: Builtin.NativeObject = getKlass() // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
38+
39+
@_noImplicitCopy var myMoveOnly: Builtin.NativeObject { // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
40+
return getKlass()
41+
}
42+
43+
func foo<T>(@_noImplicitCopy _ t: T) { // expected-error {{@_noImplicitCopy may only be used on 'var' declarations}}
44+
}
45+
}
46+
47+
struct MyGenericStruct<T> {
48+
func foo(@_noImplicitCopy _ t: T) { // expected-error {{@_noImplicitCopy may only be used on 'var' declarations}}
49+
}
50+
}
51+
52+
protocol P {
53+
@_noImplicitCopy var x: Builtin.NativeObject { get } // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
54+
}
55+
56+
func foo<T>(@_noImplicitCopy _ t: T) { // expected-error {{@_noImplicitCopy may only be used on 'var' declarations}}
57+
}
58+
59+
// Do not error on class fields. The noImplicitCopy field is separate from the
60+
// underlying class itself so the fact the class is not move only does not
61+
// suggest that the binding inside the class can be.
62+
class MyClass {
63+
@_noImplicitCopy var x: Builtin.NativeObject = getKlass() // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
64+
@_noImplicitCopy let y: Builtin.NativeObject = getKlass() // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
65+
66+
@_noImplicitCopy var myMoveOnly: Builtin.NativeObject { // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
67+
return getKlass()
68+
}
69+
70+
func foo<T>(@_noImplicitCopy _ t: T) { // expected-error {{@_noImplicitCopy may only be used on 'var' declarations}}
71+
}
72+
}
73+
74+
class MyGenericClass<T> {
75+
@_noImplicitCopy var x: T? = nil // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
76+
@_noImplicitCopy let y: T? = nil // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
77+
78+
@_noImplicitCopy var myMoveOnly: T? { // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
79+
return nil
80+
}
81+
82+
@_noImplicitCopy var myMoveOnly2: Builtin.NativeObject? { // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
83+
return nil
84+
}
85+
86+
func foo(@_noImplicitCopy _ t: T) { // expected-error {{@_noImplicitCopy may only be used on 'var' declarations}}
87+
}
88+
}
89+
90+
// We need to error on Enums since the case is part of the value and we do not
91+
// support move only types.
92+
enum MyEnum {
93+
case none
94+
case noImplicitCopyCase(Klass)
95+
96+
// We suport doing it on computed properties though.
97+
@_noImplicitCopy var myMoveOnly: Builtin.NativeObject { // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
98+
return getKlass()
99+
}
100+
}
101+
102+
// We need to error on Enums since the case is part of the value and we do not
103+
// support move only types.
104+
enum MyGenericEnum<T> {
105+
case none
106+
case noImplicitCopyCase(Klass)
107+
108+
// We suport doing it on computed properties though.
109+
@_noImplicitCopy var myMoveOnly: Builtin.NativeObject { // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
110+
return getKlass()
111+
}
112+
113+
// We suport doing it on computed properties though.
114+
@_noImplicitCopy var myMoveOnly2: T? { // expected-error {{'@_noImplicitCopy' attribute can only be applied to local lets}}
115+
return nil
116+
}
117+
}
118+
119+
struct UnsafePointerWithOwner<T> {
120+
var owner: AnyObject? = nil
121+
var data: UnsafePointer<T>? = nil
122+
123+
func doNothing() {}
124+
}
125+
126+
func useUnsafePointerWithOwner<T>(_ x: UnsafePointerWithOwner<T>) {
127+
// We allow for this here (even without opaque values, since we check this
128+
// at the SIL level in SILGen).
129+
@_noImplicitCopy let y = x
130+
y.doNothing()
131+
let z = y
132+
print(z)
133+
}
134+
135+
func useGeneric<T>(_ x: T) {
136+
// We allow for this here (even without opaque values, since we check this
137+
// at the SIL level in SILGen).
138+
@_noImplicitCopy let y = x
139+
let z = y
140+
print(z)
141+
}

0 commit comments

Comments
 (0)