Skip to content

Commit a589b4a

Browse files
committed
[moveOnly] Add an @_noImplicitCopy decl attribute and allow it to be only attached to local lets.
Some notes: 1. This is not actually wired up to any part of codegen. Instead, this PR just has the code necessary to parse the attribute and to ensure that we use it only on local lets. The rest will come in subsequent commits. 2. I am allowing for the attribute to be attached to generic things in Sema since we do not have enough information in the TypeChecker to distinguish in between structs with a type parameter but that have all non-generic stored vars from one with generic stored vars. We can only support the later with opaque values but the former we can support without opaque values (and is one of the use cases we are interested in). rdar://83957088
1 parent 6e11611 commit a589b4a

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)