Skip to content

Commit 2848482

Browse files
committed
Merge pull request #2354 from dduan/se0035-pr
2 parents 540619e + 8f955dc commit 2848482

File tree

11 files changed

+198
-34
lines changed

11 files changed

+198
-34
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2133,6 +2133,18 @@ NOTE(transitive_capture_through_here,none,
21332133
"%0, declared here, captures %1",
21342134
(Identifier, Identifier))
21352135

2136+
ERROR(closure_implicit_capture_without_noescape,none,
2137+
"closure cannot implicitly capture an inout parameter unless @noescape",
2138+
())
2139+
ERROR(closure_implicit_capture_mutating_self,none,
2140+
"closure cannot implicitly capture a mutating self parameter",
2141+
())
2142+
ERROR(nested_function_with_implicit_capture_argument,none,
2143+
"nested function with %select{a |}0implicitly captured inout "
2144+
"parameter%select{|s}0 can only be used as a @noescape argument", (bool))
2145+
ERROR(nested_function_escaping_inout_capture,none,
2146+
"nested function cannot capture inout parameter and escape", ())
2147+
21362148
WARNING(recursive_accessor_reference,none,
21372149
"attempting to %select{access|modify}1 %0 within its own "
21382150
"%select{getter|setter}1", (Identifier, bool))

lib/Sema/CSApply.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5827,6 +5827,42 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
58275827
covariantResultType);
58285828
}
58295829

5830+
// Extract all arguments.
5831+
auto *CEA = arg;
5832+
if (auto *TSE = dyn_cast<TupleShuffleExpr>(CEA))
5833+
CEA = TSE->getSubExpr();
5834+
// The argument is either a ParenExpr or TupleExpr.
5835+
ArrayRef<Expr *> arguments;
5836+
ArrayRef<TypeBase *> types;
5837+
5838+
if (auto *TE = dyn_cast<TupleExpr>(CEA))
5839+
arguments = TE->getElements();
5840+
else if (auto *PE = dyn_cast<ParenExpr>(CEA))
5841+
arguments = PE->getSubExpr();
5842+
else
5843+
arguments = apply->getArg();
5844+
5845+
for (auto arg: arguments) {
5846+
bool isNoEscape = false;
5847+
while (1) {
5848+
if (auto AFT = arg->getType()->getAs<AnyFunctionType>()) {
5849+
isNoEscape = isNoEscape || AFT->isNoEscape();
5850+
}
5851+
5852+
if (auto conv = dyn_cast<ImplicitConversionExpr>(arg))
5853+
arg = conv->getSubExpr();
5854+
else if (auto *PE = dyn_cast<ParenExpr>(arg))
5855+
arg = PE->getSubExpr();
5856+
else
5857+
break;
5858+
}
5859+
if (!isNoEscape) {
5860+
if (auto DRE = dyn_cast<DeclRefExpr>(arg))
5861+
if (auto FD = dyn_cast<FuncDecl>(DRE->getDecl())) {
5862+
tc.addEscapingFunctionAsArgument(FD, apply);
5863+
}
5864+
}
5865+
}
58305866
return result;
58315867
}
58325868

lib/Sema/TypeCheckExpr.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,25 @@ namespace {
973973
if (!validateForwardCapture(DRE->getDecl()))
974974
return { false, DRE };
975975

976+
bool isInOut = D->hasInterfaceType()
977+
&& isa<InOutType>(D->getInterfaceType().getPointer());
978+
bool isNested = false;
979+
if (auto f = AFR.getAbstractFunctionDecl()) {
980+
isNested = f->getDeclContext()->getContextKind() ==
981+
DeclContextKind::AbstractFunctionDecl;
982+
}
983+
984+
if (isInOut && !AFR.isKnownNoEscape() && !isNested) {
985+
if (D->getNameStr() == "self") {
986+
TC.diagnose(DRE->getLoc(),
987+
diag::closure_implicit_capture_mutating_self);
988+
} else {
989+
TC.diagnose(DRE->getLoc(),
990+
diag::closure_implicit_capture_without_noescape);
991+
}
992+
return { false, DRE };
993+
}
994+
976995
// We're going to capture this, compute flags for the capture.
977996
unsigned Flags = 0;
978997

@@ -1215,6 +1234,28 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) {
12151234
FindCapturedVars finder(*this, Captures, GenericParamCaptureLoc, AFR);
12161235
AFR.getBody()->walk(finder);
12171236

1237+
unsigned inoutCount = 0;
1238+
for (auto C: Captures) {
1239+
if (auto type = C.getDecl()->getInterfaceType())
1240+
if (isa<InOutType>(type.getPointer()))
1241+
inoutCount++;
1242+
}
1243+
1244+
if (inoutCount > 0) {
1245+
if (auto e = AFR.getAbstractFunctionDecl()) {
1246+
for (auto returnOccurance: getEscapingFunctionAsReturnValue(e)) {
1247+
diagnose(returnOccurance->getReturnLoc(),
1248+
diag::nested_function_escaping_inout_capture);
1249+
}
1250+
auto occurances = getEscapingFunctionAsArgument(e);
1251+
for (auto occurance: occurances) {
1252+
diagnose(occurance->getLoc(),
1253+
diag::nested_function_with_implicit_capture_argument,
1254+
inoutCount > 1);
1255+
}
1256+
}
1257+
}
1258+
12181259
if (AFR.hasType() && !AFR.isObjC()) {
12191260
finder.checkType(AFR.getType(), AFR.getLoc());
12201261
}

lib/Sema/TypeCheckStmt.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,9 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
441441
tryDiagnoseUnnecessaryCastOverOptionSet(TC.Context, E, ResultTy,
442442
DC->getParentModule());
443443
}
444-
444+
if (auto DRE = dyn_cast<DeclRefExpr>(E))
445+
if (auto FD = dyn_cast<FuncDecl>(DRE->getDecl()))
446+
TC.addEscapingFunctionAsReturnValue(FD, RS);
445447
return RS;
446448
}
447449

lib/Sema/TypeChecker.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,59 @@ class TypeChecker final : public LazyResolver {
512512
/// computed.
513513
llvm::DenseMap<AnyFunctionRef, std::vector<Expr*>> LocalCFunctionPointers;
514514

515+
private:
516+
/// Return statements with functions as return values.
517+
llvm::DenseMap<AbstractFunctionDecl *, llvm::DenseSet<ReturnStmt *>>
518+
FunctionAsReturnValue;
519+
520+
/// Function apply expressions with a certain function as an arugment.
521+
llvm::DenseMap<AbstractFunctionDecl *, llvm::DenseSet<ApplyExpr *>>
522+
FunctionAsEscapingArg;
523+
524+
public:
525+
/// Record an occurence of a function that captures inout values as an
526+
/// argument.
527+
///
528+
/// \param decl the function that occurs as an arugment.
529+
///
530+
/// \param apply the expression in which the function appears.
531+
void addEscapingFunctionAsArgument(AbstractFunctionDecl *decl,
532+
ApplyExpr *apply) {
533+
FunctionAsEscapingArg[decl].insert(apply);
534+
}
535+
536+
/// Find occurences of a function that captures inout values as arguments.
537+
///
538+
/// \param decl the function that occurs as an arugment.
539+
///
540+
/// \returns Expressions in which the function appears as arguments.
541+
llvm::DenseSet<ApplyExpr *> &
542+
getEscapingFunctionAsArgument(AbstractFunctionDecl *decl) {
543+
return FunctionAsEscapingArg[decl];
544+
}
545+
546+
/// Record an occurence of a function that captures inout values as a return
547+
/// value
548+
///
549+
/// \param decl the function that occurs as a return value.
550+
///
551+
/// \param stmt the expression in which the function appears.
552+
void addEscapingFunctionAsReturnValue(AbstractFunctionDecl *decl,
553+
ReturnStmt *stmt) {
554+
FunctionAsReturnValue[decl].insert(stmt);
555+
}
556+
557+
/// Find occurences of a function that captures inout values as return
558+
/// values.
559+
///
560+
/// \param decl the function that occurs as a return value.
561+
///
562+
/// \returns Expressions in which the function appears as arguments.
563+
llvm::DenseSet<ReturnStmt *> &
564+
getEscapingFunctionAsReturnValue(AbstractFunctionDecl *decl) {
565+
return FunctionAsReturnValue[decl];
566+
}
567+
515568
private:
516569
Type IntLiteralType;
517570
Type FloatLiteralType;

test/DebugInfo/inout.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,29 @@
55

66
// LValues are direct values, too. They are reference types, though.
77

8-
func Close(_ fn: () -> Int64) { fn() }
8+
func Close(_ fn: @noescape () -> Int64) { fn() }
99
typealias MyFloat = Float
1010

1111
// CHECK: define hidden void @_TF5inout13modifyFooHeap
1212
// CHECK: %[[ALLOCA:.*]] = alloca %Vs5Int64*
13-
// CHECK: %[[ALLOCB:.*]] = alloca %Sf
1413
// CHECK: call void @llvm.dbg.declare(metadata
1514
// CHECK-SAME: %[[ALLOCA]], metadata ![[A:[0-9]+]]
16-
// CHECK: call void @llvm.dbg.declare(metadata
17-
// CHECK-SAME: %[[ALLOCB]], metadata ![[B:[0-9]+]], metadata !{{[0-9]+}})
1815

1916
// Closure with promoted capture.
20-
// PROMO-CHECK: define {{.*}}@_TTSf2i___TFF5inout13modifyFooHeapFTRVs5Int64Sf_T_U_FT_S0_
21-
// PROMO-CHECK: call void @llvm.dbg.declare(metadata {{(i32|i64)}}* %
17+
// PROMO-CHECK: define {{.*}}@_TFF5inout13modifyFooHeapFTRVs5Int64Sf_T_U_FT_S0_
18+
// PROMO-CHECK: call void @llvm.dbg.declare(metadata %Vs5Int64** %
2219
// PROMO-CHECK-SAME: metadata ![[A1:[0-9]+]], metadata ![[EMPTY_EXPR:[0-9]+]])
2320

24-
// PROMO-CHECK: ![[INT:.*]] = !DICompositeType({{.*}}identifier: "_TtVs5Int64"
2521
// PROMO-CHECK: ![[EMPTY_EXPR]] = !DIExpression()
22+
// PROMO-CHECK: ![[INT:.*]] = !DICompositeType({{.*}}identifier: "_TtRVs5Int64"
2623
// PROMO-CHECK: ![[A1]] = !DILocalVariable(name: "a", arg: 1
2724
// PROMO-CHECK-SAME: type: ![[INT]]
2825
func modifyFooHeap(_ a: inout Int64,
2926
// CHECK-DAG: ![[A]] = !DILocalVariable(name: "a", arg: 1{{.*}} line: [[@LINE-1]],{{.*}} type: ![[RINT:[0-9]+]]
3027
// CHECK-DAG: ![[RINT]] = !DICompositeType({{.*}}identifier: "_TtRVs5Int64"
3128
_ b: MyFloat)
3229
{
33-
var b = b
30+
let b = b
3431
if (b > 2.71) {
3532
a = a + 12// Set breakpoint here
3633
}

test/Interpreter/capture_inout.swift

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
// RUN: %target-run-simple-swift | FileCheck %s
22
// REQUIRES: executable_test
33

4-
func foo(_ x: inout Int) -> () -> Int {
4+
func foo(_ x: inout Int) {
55
func bar() -> Int {
66
x += 1
77
return x
88
}
99
bar()
10-
return bar
1110
}
1211

1312
var x = 219
14-
var f = foo(&x)
15-
print(x) // CHECK: 220
16-
print(f()) // CHECK: 221
17-
print(f()) // CHECK: 222
18-
print(f()) // CHECK: 223
13+
foo(&x)
1914
print(x) // CHECK: 220

test/SILGen/capture_inout.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ typealias Int = Builtin.Int64
66
// CHECK: bb0([[X_INOUT:%.*]] : $*Builtin.Int64):
77
// CHECK: [[X_LOCAL:%.*]] = alloc_box $Builtin.Int64
88
// CHECK: [[FUNC:%.*]] = function_ref [[CLOSURE:@.*]] : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64
9-
// CHECK: partial_apply [[FUNC]]([[X_LOCAL]])
9+
// CHECK: apply [[FUNC]]([[X_LOCAL]])
1010
// CHECK: }
1111
// CHECK: sil shared [[CLOSURE]] : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64
12-
func foo(x: inout Int) -> () -> Int {
12+
func foo(x: inout Int) {
1313
func bar() -> Int {
1414
return x
1515
}
16-
return bar
16+
bar()
1717
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-swift-frontend %s -emit-ir -verify
2+
3+
// Verify we don't crash on this.
4+
// rdar://15595118
5+
infix operator ~> { precedence 255 }
6+
protocol Target {}
7+
8+
func ~> <Target, Arg0, Result>(x: inout Target, f: (_: inout Target, _: Arg0) -> Result) -> (Arg0) -> Result {
9+
return { f(&x, $0) } // expected-error {{closure cannot implicitly capture an inout parameter unless @noescape}}
10+
}
11+
12+
func ~> (x: inout Int, f: (_: inout Int, _: Target) -> Target) -> (Target) -> Target {
13+
return { f(&x, $0) } // expected-error {{closure cannot implicitly capture an inout parameter unless @noescape}}
14+
}
15+
Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -emit-sil %s | FileCheck %s
1+
// RUN: %target-swift-frontend -emit-sil %s -verify | FileCheck %s
22

33
// Make sure that materializations of computed properties such as 'false' and
44
// 'true' don't leave needless stack allocations. <rdar://problem/15272642>
@@ -10,17 +10,3 @@
1010
func g() {
1111
if false {}
1212
}
13-
14-
// Verify we don't crash on this.
15-
// rdar://15595118
16-
infix operator ~> { precedence 255 }
17-
protocol Target {}
18-
19-
func ~> <Target, Arg0, Result>(x: inout Target, f: (_: inout Target, _: Arg0) -> Result) -> (Arg0) -> Result {
20-
return { f(&x, $0) }
21-
}
22-
23-
func ~> (x: inout Int, f: (_: inout Int, _: Target) -> Target) -> (Target) -> Target {
24-
return { f(&x, $0) }
25-
}
26-

0 commit comments

Comments
 (0)