Skip to content

Commit 4077c75

Browse files
committed
SILGen: Emit references to noncopyable global storage directly as a borrow.
Later analyses are too conservative to remove a copy, but it should be fairly safe to elide the copy for noncopyable globals, since accesses are tightly scoped and dynamically checked, so consumes aren't possible, and borrows and inout accesses of mutable globals are dynamically guarded and not subject to exclusivity checks. rdar://114329759
1 parent 9f2acb2 commit 4077c75

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3278,7 +3278,20 @@ Expr *SILGenFunction::findStorageReferenceExprForMoveOnly(Expr *argExpr,
32783278
if (auto *declRef = dyn_cast<DeclRefExpr>(argExpr)) {
32793279
assert(!declRef->getType()->is<LValueType>() &&
32803280
"Shouldn't ever have an lvalue type here!");
3281-
return nullptr;
3281+
3282+
// Proceed if the storage references a global or static let.
3283+
// TODO: We should treat any storage reference as a borrow, it seems, but
3284+
// that currently disrupts what the move checker expects. It would also
3285+
// be valuable to borrow copyable global lets, but this is a targeted
3286+
// fix to allow noncopyable globals to work properly.
3287+
bool isGlobal = false;
3288+
if (auto vd = dyn_cast<VarDecl>(declRef->getDecl())) {
3289+
isGlobal = vd->isGlobalStorage();
3290+
}
3291+
3292+
if (!isGlobal) {
3293+
return nullptr;
3294+
}
32823295
}
32833296
}
32843297

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %target-swift-frontend -parse-as-library -DADDRESS_ONLY -emit-sil -verify %s
2+
// RUN: %target-swift-frontend -parse-as-library -DLOADABLE -emit-sil -verify %s
3+
// RUN: %target-swift-frontend -parse-as-library -DTRIVIAL -emit-sil -verify %s
4+
// RUN: %target-swift-frontend -parse-as-library -DEMPTY -emit-sil -verify %s
5+
6+
// RUN: %target-swift-frontend -DADDRESS_ONLY -emit-sil -verify %s
7+
// RUN: %target-swift-frontend -DLOADABLE -emit-sil -verify %s
8+
// RUN: %target-swift-frontend -DTRIVIAL -emit-sil -verify %s
9+
// RUN: %target-swift-frontend -DEMPTY -emit-sil -verify %s
10+
11+
struct Butt: ~Copyable {
12+
#if ADDRESS_ONLY
13+
var x: Any
14+
#elseif LOADABLE
15+
var x: AnyObject
16+
#elseif TRIVIAL
17+
var x: Int
18+
#elseif EMPTY
19+
#else
20+
#error("pick one")
21+
#endif
22+
23+
init() { fatalError() }
24+
25+
borrowing func method() {}
26+
}
27+
28+
func freefunc(_: borrowing Butt) {}
29+
30+
let global = Butt()
31+
32+
struct StaticHolder {
33+
static let staticMember = Butt()
34+
}
35+
36+
func foo() {
37+
freefunc(global)
38+
freefunc(StaticHolder.staticMember)
39+
global.method()
40+
StaticHolder.staticMember.method()
41+
}
42+
43+
func consume(_: consuming Butt) {}
44+
45+
func tryConsume() {
46+
// FIXME: gives different diagnostics for parse-as-library vs script global
47+
consume(global) // expected-error{{}} expected-note *{{}}
48+
consume(StaticHolder.staticMember) // expected-error{{cannot be consumed}} expected-note{{consumed here}}
49+
}
50+
51+
var globalVar = Butt()
52+
53+
func mutate(_: inout Butt) {}
54+
55+
func manipulateGlobalVar() {
56+
freefunc(globalVar)
57+
mutate(&globalVar)
58+
// FIXME: gives different diagnostics for parse-as-library vs script global
59+
consume(globalVar) // expected-error{{consume}}
60+
}

0 commit comments

Comments
 (0)