Skip to content

Commit a02633a

Browse files
authored
Merge pull request #80103 from DougGregor/strict-safety-more-minor-cleanups
2 parents 012ac5d + 9570e1e commit a02633a

File tree

6 files changed

+120
-4
lines changed

6 files changed

+120
-4
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8204,6 +8204,8 @@ NOTE(note_use_of_unsafe_conformance_is_unsafe,none,
82048204
(Type, const ValueDecl *))
82058205
NOTE(note_unsafe_storage,none,
82068206
"%kindbase1 involves unsafe type %2", (bool, const ValueDecl *, Type))
8207+
NOTE(note_unsafe_temporarily_escaping,none,
8208+
"'withoutActuallyEscaping' function of type %0 is unsafe", (Type))
82078209

82088210
GROUPED_WARNING(decl_unsafe_storage,StrictMemorySafety,none,
82098211
"%kindbase0 has storage involving unsafe types",
@@ -8240,9 +8242,9 @@ GROUPED_WARNING(unsafe_without_unsafe,StrictMemorySafety,none,
82408242
"expression uses unsafe constructs but is not marked with 'unsafe'", ())
82418243
GROUPED_WARNING(for_unsafe_without_unsafe,StrictMemorySafety,none,
82428244
"for-in loop uses unsafe constructs but is not marked with 'unsafe'", ())
8243-
WARNING(no_unsafe_in_unsafe,none,
8245+
GROUPED_WARNING(no_unsafe_in_unsafe,StrictMemorySafety,none,
82448246
"no unsafe operations occur within 'unsafe' expression", ())
8245-
WARNING(no_unsafe_in_unsafe_for,none,
8247+
GROUPED_WARNING(no_unsafe_in_unsafe_for,StrictMemorySafety,none,
82468248
"no unsafe operations occur within 'unsafe' for-in loop", ())
82478249
NOTE(make_subclass_unsafe,none,
82488250
"make class %0 @unsafe to allow unsafe overrides of safe superclass methods",

include/swift/AST/UnsafeUse.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define SWIFT_AST_UNSAFEUSE_H
1515

1616
#include "swift/AST/Decl.h"
17+
#include "swift/AST/Expr.h"
1718
#include "swift/AST/ProtocolConformance.h"
1819
#include "swift/AST/ProtocolConformanceRef.h"
1920
#include "swift/AST/Type.h"
@@ -54,6 +55,9 @@ class UnsafeUse {
5455
CallToUnsafe,
5556
/// A @preconcurrency import.
5657
PreconcurrencyImport,
58+
/// A use of withoutActuallyEscaping that lacks enforcement that the
59+
/// value didn't actually escape.
60+
TemporarilyEscaping,
5761
};
5862

5963
private:
@@ -85,6 +89,8 @@ class UnsafeUse {
8589
const void *location;
8690
} entity;
8791

92+
MakeTemporarilyEscapableExpr *temporarilyEscaping;
93+
8894
const ImportDecl *importDecl;
8995
} storage;
9096

@@ -195,6 +201,12 @@ class UnsafeUse {
195201
decl, type, location);
196202
}
197203

204+
static UnsafeUse forTemporarilyEscaping(MakeTemporarilyEscapableExpr *expr) {
205+
UnsafeUse result(TemporarilyEscaping);
206+
result.storage.temporarilyEscaping = expr;
207+
return result;
208+
}
209+
198210
static UnsafeUse forPreconcurrencyImport(const ImportDecl *importDecl) {
199211
UnsafeUse result(PreconcurrencyImport);
200212
result.storage.importDecl = importDecl;
@@ -230,6 +242,9 @@ class UnsafeUse {
230242
return SourceLoc(
231243
llvm::SMLoc::getFromPointer((const char *)storage.entity.location));
232244

245+
case TemporarilyEscaping:
246+
return storage.temporarilyEscaping->getLoc();
247+
233248
case PreconcurrencyImport:
234249
return storage.importDecl->getLoc();
235250
}
@@ -240,6 +255,7 @@ class UnsafeUse {
240255
switch (getKind()) {
241256
case Override:
242257
case Witness:
258+
case TemporarilyEscaping:
243259
case PreconcurrencyImport:
244260
// Cannot replace location.
245261
return;
@@ -283,6 +299,7 @@ class UnsafeUse {
283299
return storage.entity.decl;
284300

285301
case UnsafeConformance:
302+
case TemporarilyEscaping:
286303
return nullptr;
287304

288305
case PreconcurrencyImport:
@@ -315,6 +332,7 @@ class UnsafeUse {
315332
case CallToUnsafe:
316333
case UnsafeConformance:
317334
case PreconcurrencyImport:
335+
case TemporarilyEscaping:
318336
return nullptr;
319337
}
320338
}
@@ -341,6 +359,9 @@ class UnsafeUse {
341359
case ReferenceToUnsafeThroughTypealias:
342360
case CallToUnsafe:
343361
return storage.entity.type;
362+
363+
case TemporarilyEscaping:
364+
return storage.temporarilyEscaping->getOpaqueValue()->getType();
344365
}
345366
}
346367

@@ -365,6 +386,7 @@ class UnsafeUse {
365386
case ReferenceToUnsafeStorage:
366387
case ReferenceToUnsafeThroughTypealias:
367388
case CallToUnsafe:
389+
case TemporarilyEscaping:
368390
case PreconcurrencyImport:
369391
return ProtocolConformanceRef::forInvalid();
370392
}

lib/Sema/TypeCheckEffects.cpp

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,10 @@ class EffectsHandlingWalker : public ASTWalker {
644644
break;
645645
}
646646
}
647+
} else if (auto TE = dyn_cast<MakeTemporarilyEscapableExpr>(E)) {
648+
recurse = asImpl().checkTemporarilyEscapable(TE);
647649
}
650+
648651
// Error handling validation (via checkTopLevelEffects) happens after
649652
// type checking. If an unchecked expression is still around, the code was
650653
// invalid.
@@ -1090,6 +1093,15 @@ class Classification {
10901093
return result;
10911094
}
10921095

1096+
/// Return a potentially-unsafe classification by recording all of the
1097+
/// provided unsafe uses.
1098+
static Classification forUnsafe(ArrayRef<UnsafeUse> unsafeUses) {
1099+
Classification result;
1100+
for (const auto &unsafeUse : unsafeUses)
1101+
result.recordUnsafeUse(unsafeUse);
1102+
return result;
1103+
}
1104+
10931105
/// Return a classification for a given declaration reference.
10941106
static Classification
10951107
forDeclRef(ConcreteDeclRef declRef, ConditionalEffectKind conditionalKind,
@@ -1856,6 +1868,38 @@ class ApplyClassifier {
18561868
return result;
18571869
}
18581870

1871+
Classification classifyTemporarilyEscapable(MakeTemporarilyEscapableExpr *E) {
1872+
auto opaqueValue = E->getOpaqueValue();
1873+
if (!opaqueValue)
1874+
return Classification();
1875+
1876+
auto type = opaqueValue->getType();
1877+
if (!type)
1878+
return Classification();
1879+
1880+
auto fnType = type->getAs<AnyFunctionType>();
1881+
if (!fnType)
1882+
return Classification();
1883+
1884+
// Runtime safety checks for temporarily-escapable functions aren't
1885+
// always available.
1886+
switch (fnType->getRepresentation()) {
1887+
case FunctionTypeRepresentation::Swift:
1888+
// Swift performs runtime checking to ensure that the function did not
1889+
// escape.
1890+
return Classification();
1891+
1892+
case FunctionTypeRepresentation::Block:
1893+
return Classification::forUnsafe({UnsafeUse::forTemporarilyEscaping(E)});
1894+
1895+
case FunctionTypeRepresentation::Thin:
1896+
case FunctionTypeRepresentation::CFunctionPointer:
1897+
// Thin and C functions cannot carry any state with them, so escaping
1898+
// the pointers does not introduce a memory-safety issue.
1899+
return Classification();
1900+
}
1901+
}
1902+
18591903
private:
18601904
/// Classify a throwing or async function according to our local
18611905
/// knowledge of its implementation.
@@ -2073,6 +2117,10 @@ class ApplyClassifier {
20732117
return ShouldRecurse;
20742118
}
20752119

2120+
ShouldRecurse_t checkTemporarilyEscapable(MakeTemporarilyEscapableExpr *E) {
2121+
return ShouldRecurse;
2122+
}
2123+
20762124
ConditionalEffectKind checkExhaustiveDoBody(DoCatchStmt *S) {
20772125
// All errors thrown by the do body are caught, but any errors thrown
20782126
// by the catch bodies are bounded by the throwing kind of the do body.
@@ -2214,6 +2262,10 @@ class ApplyClassifier {
22142262
return ShouldRecurse;
22152263
}
22162264

2265+
ShouldRecurse_t checkTemporarilyEscapable(MakeTemporarilyEscapableExpr *E) {
2266+
return ShouldRecurse;
2267+
}
2268+
22172269
void visitExprPre(Expr *expr) { return; }
22182270
};
22192271

@@ -2327,6 +2379,11 @@ class ApplyClassifier {
23272379
return ShouldRecurse;
23282380
}
23292381

2382+
ShouldRecurse_t checkTemporarilyEscapable(MakeTemporarilyEscapableExpr *E) {
2383+
classification.merge(Self.classifyTemporarilyEscapable(E));
2384+
return ShouldRecurse;
2385+
}
2386+
23302387
void visitExprPre(Expr *expr) { return; }
23312388
};
23322389

@@ -3774,6 +3831,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
37743831
return ShouldRecurse;
37753832
}
37763833

3834+
ShouldRecurse_t checkTemporarilyEscapable(MakeTemporarilyEscapableExpr *E) {
3835+
auto classification = getApplyClassifier().
3836+
classifyTemporarilyEscapable(E);
3837+
checkEffectSite(E, /*requiresTry=*/false, classification);
3838+
return ShouldRecurse;
3839+
}
3840+
37773841
ConditionalEffectKind checkExhaustiveDoBody(DoCatchStmt *S) {
37783842
// This is a context where errors are handled.
37793843
ContextScope scope(*this, CurContext.withHandlesErrors());
@@ -4356,7 +4420,8 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
43564420
return;
43574421
}
43584422

4359-
Ctx.Diags.diagnose(E->getUnsafeLoc(), diag::no_unsafe_in_unsafe);
4423+
Ctx.Diags.diagnose(E->getUnsafeLoc(), diag::no_unsafe_in_unsafe)
4424+
.fixItRemove(E->getUnsafeLoc());
43604425
}
43614426

43624427
std::pair<SourceLoc, std::string>

lib/Sema/TypeCheckUnsafe.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ void swift::diagnoseUnsafeUse(const UnsafeUse &use) {
154154
return;
155155
}
156156

157+
case UnsafeUse::TemporarilyEscaping: {
158+
Type type = use.getType();
159+
ASTContext &ctx = type->getASTContext();
160+
ctx.Diags.diagnose(
161+
use.getLocation(), diag::note_unsafe_temporarily_escaping, type);
162+
return;
163+
}
164+
157165
case UnsafeUse::PreconcurrencyImport: {
158166
auto importDecl = cast<ImportDecl>(use.getDecl());
159167
importDecl->diagnose(diag::preconcurrency_import_unsafe);

test/Unsafe/safe.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ func unsafeFun() {
195195
acceptBoolsUnsafeLabel(unsafe: unsafe, unsafe)
196196

197197
let color: Color
198-
// expected-warning@+1{{no unsafe operations occur within 'unsafe' expression}}
198+
// expected-warning@+1{{no unsafe operations occur within 'unsafe' expression}}{{11-18=}}
199199
color = unsafe .red
200200
_ = color
201201

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-typecheck-verify-swift -strict-memory-safety
2+
3+
// REQUIRES: objc_interop
4+
5+
func callMe(body: @escaping @convention(block) () -> Void) { }
6+
7+
func callMeSwiftly(body: @escaping () -> Void) { }
8+
9+
func testCall(body: @convention(block) () -> Void, swiftBody: () -> Void) {
10+
// expected-warning@+2{{unsafe}}{{3-3=unsafe }}
11+
// expected-note@+1{{'withoutActuallyEscaping' function of type '@convention(block) () -> Void' is unsafe}}
12+
withoutActuallyEscaping(body) { nonescapingBody in
13+
callMe(body: nonescapingBody)
14+
}
15+
16+
withoutActuallyEscaping(swiftBody) { nonescapingBody in
17+
callMeSwiftly(body: nonescapingBody)
18+
}
19+
}

0 commit comments

Comments
 (0)