Skip to content

Commit aab7350

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 31c4602 commit aab7350

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
@@ -3327,7 +3327,20 @@ Expr *SILGenFunction::findStorageReferenceExprForMoveOnly(Expr *argExpr,
33273327
if (auto *declRef = dyn_cast<DeclRefExpr>(argExpr)) {
33283328
assert(!declRef->getType()->is<LValueType>() &&
33293329
"Shouldn't ever have an lvalue type here!");
3330-
return nullptr;
3330+
3331+
// Proceed if the storage references a global or static let.
3332+
// TODO: We should treat any storage reference as a borrow, it seems, but
3333+
// that currently disrupts what the move checker expects. It would also
3334+
// be valuable to borrow copyable global lets, but this is a targeted
3335+
// fix to allow noncopyable globals to work properly.
3336+
bool isGlobal = false;
3337+
if (auto vd = dyn_cast<VarDecl>(declRef->getDecl())) {
3338+
isGlobal = vd->isGlobalStorage();
3339+
}
3340+
3341+
if (!isGlobal) {
3342+
return nullptr;
3343+
}
33313344
}
33323345
}
33333346

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)