Skip to content

Commit c9cfed2

Browse files
committed
[Strict safety] Diagnose types with unsafe storage
When a type definition involves unsafe types in its storage, require it to be explicitly marked @unsafe or @safe.
1 parent 2de9f4a commit c9cfed2

File tree

6 files changed

+113
-4
lines changed

6 files changed

+113
-4
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8149,6 +8149,18 @@ NOTE(note_reference_exclusivity_unchecked,none,
81498149
NOTE(note_use_of_unsafe_conformance_is_unsafe,none,
81508150
"@unsafe conformance of %0 to %kind1 involves unsafe code",
81518151
(Type, const ValueDecl *))
8152+
NOTE(note_unsafe_storage,none,
8153+
"%kindbase1 involves unsafe type %2", (bool, const ValueDecl *, Type))
8154+
8155+
GROUPED_WARNING(decl_unsafe_storage,Unsafe,none,
8156+
"%kindbase0 has storage involving unsafe types",
8157+
(const Decl *))
8158+
NOTE(decl_storage_mark_unsafe,none,
8159+
"add '@unsafe' if this type is also unsafe to use",
8160+
())
8161+
NOTE(decl_storage_mark_safe,none,
8162+
"add '@safe' if this type encapsulates the unsafe storage in "
8163+
"a safe interface", ())
81528164

81538165
GROUPED_WARNING(conformance_involves_unsafe,Unsafe,none,
81548166
"conformance of %0 to %kind1 involves unsafe code; use '@unsafe' to "

include/swift/AST/UnsafeUse.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ class UnsafeUse {
4545
NonisolatedUnsafe,
4646
/// A reference to an unsafe declaration.
4747
ReferenceToUnsafe,
48+
/// A reference to an unsafe storage.
49+
ReferenceToUnsafeStorage,
4850
/// A reference to a typealias that is not itself unsafe, but has
4951
/// an unsafe underlying type.
5052
ReferenceToUnsafeThroughTypealias,
@@ -113,6 +115,7 @@ class UnsafeUse {
113115
kind == ExclusivityUnchecked ||
114116
kind == NonisolatedUnsafe ||
115117
kind == ReferenceToUnsafe ||
118+
kind == ReferenceToUnsafeStorage ||
116119
kind == ReferenceToUnsafeThroughTypealias ||
117120
kind == CallToUnsafe);
118121

@@ -179,6 +182,12 @@ class UnsafeUse {
179182
decl, type, location);
180183
}
181184

185+
static UnsafeUse forReferenceToUnsafeStorage(const Decl *decl,
186+
Type type,
187+
SourceLoc location) {
188+
return forReference(ReferenceToUnsafeStorage, decl, type, location);
189+
}
190+
182191
static UnsafeUse forReferenceToUnsafeThroughTypealias(const Decl *decl,
183192
Type type,
184193
SourceLoc location) {
@@ -215,6 +224,7 @@ class UnsafeUse {
215224
case ExclusivityUnchecked:
216225
case NonisolatedUnsafe:
217226
case ReferenceToUnsafe:
227+
case ReferenceToUnsafeStorage:
218228
case ReferenceToUnsafeThroughTypealias:
219229
case CallToUnsafe:
220230
return SourceLoc(
@@ -246,6 +256,7 @@ class UnsafeUse {
246256
case ExclusivityUnchecked:
247257
case NonisolatedUnsafe:
248258
case ReferenceToUnsafe:
259+
case ReferenceToUnsafeStorage:
249260
case ReferenceToUnsafeThroughTypealias:
250261
case CallToUnsafe:
251262
storage.entity.location = loc.getOpaquePointerValue();
@@ -266,6 +277,7 @@ class UnsafeUse {
266277
case ExclusivityUnchecked:
267278
case NonisolatedUnsafe:
268279
case ReferenceToUnsafe:
280+
case ReferenceToUnsafeStorage:
269281
case ReferenceToUnsafeThroughTypealias:
270282
case CallToUnsafe:
271283
return storage.entity.decl;
@@ -299,6 +311,7 @@ class UnsafeUse {
299311
case NonisolatedUnsafe:
300312
case ReferenceToUnsafe:
301313
case ReferenceToUnsafeThroughTypealias:
314+
case ReferenceToUnsafeStorage:
302315
case CallToUnsafe:
303316
case UnsafeConformance:
304317
case PreconcurrencyImport:
@@ -324,6 +337,7 @@ class UnsafeUse {
324337
case ExclusivityUnchecked:
325338
case NonisolatedUnsafe:
326339
case ReferenceToUnsafe:
340+
case ReferenceToUnsafeStorage:
327341
case ReferenceToUnsafeThroughTypealias:
328342
case CallToUnsafe:
329343
return storage.entity.type;
@@ -348,6 +362,7 @@ class UnsafeUse {
348362
case ExclusivityUnchecked:
349363
case NonisolatedUnsafe:
350364
case ReferenceToUnsafe:
365+
case ReferenceToUnsafeStorage:
351366
case ReferenceToUnsafeThroughTypealias:
352367
case CallToUnsafe:
353368
case PreconcurrencyImport:

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2387,12 +2387,19 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
23872387
"`" + VD->getBaseName().userFacingName().str() + "`");
23882388
}
23892389

2390-
// Expand extension macros.
23912390
if (auto *nominal = dyn_cast<NominalTypeDecl>(VD)) {
2391+
// Expand extension macros.
23922392
(void)evaluateOrDefault(
23932393
Ctx.evaluator,
23942394
ExpandExtensionMacros{nominal},
23952395
{ });
2396+
2397+
// If strict memory safety checking is enabled, check the storage
2398+
// of the nominal type.
2399+
if (Ctx.LangOpts.hasFeature(Feature::WarnUnsafe) &&
2400+
!isa<ProtocolDecl>(nominal)) {
2401+
checkUnsafeStorage(nominal);
2402+
}
23962403
}
23972404
}
23982405
}

lib/Sema/TypeCheckUnsafe.cpp

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "TypeCheckAvailability.h"
1818
#include "TypeCheckConcurrency.h"
19+
#include "TypeCheckInvertible.h"
1920
#include "TypeCheckType.h"
2021
#include "TypeCheckUnsafe.h"
2122

@@ -121,6 +122,7 @@ void swift::diagnoseUnsafeUse(const UnsafeUse &use) {
121122
}
122123

123124
case UnsafeUse::ReferenceToUnsafe:
125+
case UnsafeUse::ReferenceToUnsafeStorage:
124126
case UnsafeUse::CallToUnsafe: {
125127
bool isCall = use.getKind() == UnsafeUse::CallToUnsafe;
126128
auto decl = cast_or_null<ValueDecl>(use.getDecl());
@@ -133,7 +135,8 @@ void swift::diagnoseUnsafeUse(const UnsafeUse &use) {
133135
[&](Type specificType) {
134136
ctx.Diags.diagnose(
135137
loc,
136-
diag::note_reference_to_unsafe_typed_decl,
138+
use.getKind() == UnsafeUse::ReferenceToUnsafeStorage ? diag::note_unsafe_storage
139+
: diag::note_reference_to_unsafe_typed_decl,
137140
isCall, decl, specificType);
138141
});
139142
} else if (type) {
@@ -376,3 +379,61 @@ void swift::diagnoseUnsafeType(ASTContext &ctx, SourceLoc loc, Type type,
376379

377380
diagnose(specificType ? specificType : type);
378381
}
382+
383+
void swift::checkUnsafeStorage(NominalTypeDecl *nominal) {
384+
// If the type is marked explicitly with @safe or @unsafe, there's nothing
385+
// to check.
386+
switch (nominal->getExplicitSafety()) {
387+
case ExplicitSafety::Safe:
388+
case ExplicitSafety::Unsafe:
389+
return;
390+
391+
case ExplicitSafety::Unspecified:
392+
break;
393+
}
394+
395+
// Visitor that finds unsafe storage.
396+
class UnsafeStorageVisitor: public StorageVisitor {
397+
ASTContext &ctx;
398+
SmallVectorImpl<UnsafeUse> &unsafeUses;
399+
400+
public:
401+
UnsafeStorageVisitor(ASTContext &ctx, SmallVectorImpl<UnsafeUse> &unsafeUses)
402+
: ctx(ctx), unsafeUses(unsafeUses) { }
403+
404+
bool operator()(VarDecl *property, Type propertyType) override {
405+
diagnoseUnsafeType(ctx, property->getLoc(), propertyType, [&](Type type) {
406+
unsafeUses.push_back(
407+
UnsafeUse::forReferenceToUnsafeStorage(
408+
property, propertyType, property->getLoc()));
409+
});
410+
return false;
411+
}
412+
413+
bool operator()(EnumElementDecl *element, Type elementType) override {
414+
diagnoseUnsafeType(ctx, element->getLoc(), elementType, [&](Type type) {
415+
unsafeUses.push_back(
416+
UnsafeUse::forReferenceToUnsafeStorage(
417+
element, elementType, element->getLoc()));
418+
});
419+
return false;
420+
}
421+
};
422+
423+
// Look for any unsafe storage in this nominal type.
424+
ASTContext &ctx = nominal->getASTContext();
425+
SmallVector<UnsafeUse, 4> unsafeUses;
426+
UnsafeStorageVisitor(ctx, unsafeUses).visit(nominal, nominal->getDeclContext());
427+
428+
// If we didn't find any unsafe storage, there's nothing to do.
429+
if (unsafeUses.empty())
430+
return;
431+
432+
// Complain about this type needing @safe or @unsafe.
433+
nominal->diagnose(diag::decl_unsafe_storage, nominal);
434+
nominal->diagnose(diag::decl_storage_mark_unsafe)
435+
.fixItInsert(nominal->getAttributeInsertionLoc(false), "@unsafe ");
436+
nominal->diagnose(diag::decl_storage_mark_safe)
437+
.fixItInsert(nominal->getAttributeInsertionLoc(false), "@safe ");
438+
std::for_each(unsafeUses.begin(), unsafeUses.end(), diagnoseUnsafeUse);
439+
}

lib/Sema/TypeCheckUnsafe.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ bool isUnsafeInConformance(const ValueDecl *requirement,
7070
void diagnoseUnsafeType(ASTContext &ctx, SourceLoc loc, Type type,
7171
llvm::function_ref<void(Type)> diagnose);
7272

73+
/// Check for unsafe storage within this nominal type declaration.
74+
void checkUnsafeStorage(NominalTypeDecl *nominal);
75+
7376
}
7477

7578
#endif // SWIFT_SEMA_TYPE_CHECK_UNSAFE_H

test/Unsafe/unsafe.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,20 @@ typealias SuperUnsafe = UnsafeSuper
174174

175175
@unsafe typealias SuperUnsafe2 = UnsafeSuper
176176

177+
// expected-warning@+3{{enum 'HasUnsafeThings' has storage involving unsafe types [Unsafe]}}
178+
// expected-note@+2{{add '@unsafe' if this type is also unsafe to use}}{{1-1=@unsafe }}
179+
// expected-note@+1{{add '@safe' if this type encapsulates the unsafe storage in a safe interface}}{{1-1=@safe }}
177180
enum HasUnsafeThings {
178181

179-
case one(UnsafeSuper)
182+
case one(UnsafeSuper) // expected-note{{enum case 'one' involves unsafe type 'UnsafeSuper'}}
180183

181-
@unsafe case two(UnsafeSuper)
184+
case two(UnsafeSuper) // expected-note{{enum case 'two' involves unsafe type 'UnsafeSuper'}}
185+
}
186+
187+
// expected-warning@+3{{class 'ClassWithUnsafeStorage' has storage involving unsafe types [Unsafe]}}
188+
// expected-note@+2{{add '@unsafe' if this type is also unsafe to use}}{{1-1=@unsafe }}
189+
// expected-note@+1{{add '@safe' if this type encapsulates the unsafe storage in a safe interface}}{{1-1=@safe }}
190+
class ClassWithUnsafeStorage {
191+
var int: Int = 0
192+
var array: [UnsafeSuper]? = nil // expected-note{{property 'array' involves unsafe type 'UnsafeSuper'}}
182193
}

0 commit comments

Comments
 (0)