Skip to content

Commit 8bb5bbe

Browse files
committed
Implement an unsafe expression to cover uses of unsafe constructs
Introduce an `unsafe` expression akin to `try` and `await` that notes that there are unsafe constructs in the expression to the right-hand side. Extend the effects checker to also check for unsafety along with throwing and async operations. This will result in diagnostics like the following: 10 | func sum() -> Int { 11 | withUnsafeBufferPointer { buffer in 12 | let value = buffer[0] | | `- note: reference to unsafe subscript 'subscript(_:)' | |- warning: expression uses unsafe constructs but is not marked with 'unsafe' | `- note: reference to parameter 'buffer' involves unsafe type 'UnsafeBufferPointer<Int>' 13 | tryWithP(X()) 14 | return fastAdd(buffer.baseAddress, buffer.count) These will come with a Fix-It that inserts `unsafe` into the proper place. There's also a warning that appears when `unsafe` doesn't cover any unsafe code, making it easier to clean up extraneous `unsafe`. This approach requires that `@unsafe` be present on any declaration that involves unsafe constructs within its signature. Outside of the signature, the `unsafe` expression is used to identify unsafe code.
1 parent befa361 commit 8bb5bbe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+820
-572
lines changed

include/swift/AST/ASTBridging.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,6 +1505,11 @@ BridgedUnresolvedSpecializeExpr BridgedUnresolvedSpecializeExpr_createParsed(
15051505
BridgedSourceLoc cLAngleLoc, BridgedArrayRef cArguments,
15061506
BridgedSourceLoc cRAngleLoc);
15071507

1508+
SWIFT_NAME("BridgedUnsafeExpr.createParsed(_:unsafeLoc:subExpr:)")
1509+
BridgedUnsafeExpr BridgedUnsafeExpr_createParsed(BridgedASTContext cContext,
1510+
BridgedSourceLoc cUnsafeLoc,
1511+
BridgedExpr cSubExpr);
1512+
15081513
SWIFT_NAME("BridgedInOutExpr.createParsed(_:loc:subExpr:)")
15091514
BridgedInOutExpr BridgedInOutExpr_createParsed(BridgedASTContext cContext,
15101515
BridgedSourceLoc cLoc,

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,8 @@ ERROR(expected_expr_after_ternary_colon,none,
13711371
"expected expression after '? ... :' in ternary expression", ())
13721372
ERROR(expected_expr_after_await, none,
13731373
"expected expression after 'await'", ())
1374+
ERROR(expected_expr_after_unsafe, none,
1375+
"expected expression after 'unsafe'", ())
13741376
ERROR(expected_expr_after_move, none,
13751377
"expected expression after 'consume'", ())
13761378
ERROR(expected_expr_after_copy, none,

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4522,7 +4522,7 @@ WARNING(nan_comparison_both_nan, none,
45224522
(StringRef, bool))
45234523

45244524
// If you change this, also change enum TryKindForDiagnostics.
4525-
#define TRY_KIND_SELECT(SUB) "%select{try|try!|try?|await}" #SUB
4525+
#define TRY_KIND_SELECT(SUB) "%select{try|try!|try?|await|unsafe}" #SUB
45264526

45274527
ERROR(try_rhs,none,
45284528
"'" TRY_KIND_SELECT(0) "' cannot appear to the right of a "
@@ -8078,11 +8078,6 @@ NOTE(sending_function_result_with_sending_param_note, none,
80788078
ERROR(unsafe_attr_disabled,none,
80798079
"attribute requires '-enable-experimental-feature AllowUnsafeAttribute'", ())
80808080

8081-
GROUPED_WARNING(decl_involves_unsafe,Unsafe,none,
8082-
"%kindbase0 involves unsafe code; "
8083-
"use %select{'@unsafe' to indicate that its use is not memory-safe|"
8084-
"'@safe(unchecked)' to assert that the code is memory-safe}1",
8085-
(const Decl *, bool))
80868081
NOTE(note_reference_to_unsafe_decl,none,
80878082
"%select{reference|call}0 to unsafe %kind1",
80888083
(bool, const ValueDecl *))
@@ -8102,6 +8097,12 @@ NOTE(note_reference_exclusivity_unchecked,none,
81028097
NOTE(note_use_of_unsafe_conformance_is_unsafe,none,
81038098
"@unsafe conformance of %0 to %kind1 involves unsafe code",
81048099
(Type, const ValueDecl *))
8100+
8101+
GROUPED_WARNING(decl_signature_involves_unsafe,Unsafe,none,
8102+
"%kindbase0 has an interface that is not memory-safe; "
8103+
"use '@unsafe' to indicate that its use is unsafe",
8104+
(const Decl *))
8105+
81058106
GROUPED_WARNING(conformance_involves_unsafe,Unsafe,none,
81068107
"conformance of %0 to %kind1 involves unsafe code; use '@unsafe' to "
81078108
"indicate that the conformance is not memory-safe",
@@ -8113,44 +8114,20 @@ NOTE(note_type_witness_unsafe,none,
81138114
"unsafe type %0 cannot satisfy safe associated type %1",
81148115
(Type, DeclName))
81158116

8116-
GROUPED_WARNING(override_safe_withunsafe,Unsafe,none,
8117+
GROUPED_WARNING(override_safe_with_unsafe,Unsafe,none,
81178118
"override of safe %0 with unsafe %0", (DescriptiveDeclKind))
8118-
GROUPED_WARNING(use_of_unsafe_conformance_is_unsafe,Unsafe,none,
8119-
"@unsafe conformance of %0 to %kind1 involves unsafe code",
8120-
(Type, const ValueDecl *))
8121-
GROUPED_WARNING(reference_unowned_unsafe,Unsafe,none,
8122-
"reference to unowned(unsafe) %kind0 is unsafe", (const ValueDecl *))
8123-
GROUPED_WARNING(reference_exclusivity_unchecked,Unsafe,none,
8124-
"reference to @exclusivity(unchecked) %kind0 is unsafe", (const ValueDecl *))
8125-
8126-
GROUPED_WARNING(reference_to_nonisolated_unsafe,Unsafe,none,
8127-
"reference to nonisolated(unsafe) %kind0 is unsafe in concurrently-executing code",
8128-
(const ValueDecl *))
8129-
GROUPED_WARNING(reference_to_unsafe_decl,Unsafe,none,
8130-
"%select{reference|call}0 to unsafe %kindbase1",
8131-
(bool, const ValueDecl *))
8132-
GROUPED_WARNING(reference_to_unsafe_typed_decl,Unsafe,none,
8133-
"%select{reference|call}0 to %kindbase1 involves unsafe type %2",
8134-
(bool, const ValueDecl *, Type))
8135-
GROUPED_WARNING(reference_to_unsafe_through_typealias,Unsafe,none,
8136-
"reference to %kind0 whose underlying type involves unsafe type %1",
8137-
(const ValueDecl *, Type))
8119+
81388120
GROUPED_WARNING(preconcurrency_import_unsafe,Unsafe,none,
81398121
"@preconcurrency import is not memory-safe because it can silently "
81408122
"introduce data races; use '@safe(unchecked)' to assert that the "
81418123
"code is memory-safe", ())
8142-
NOTE(encapsulate_unsafe_in_enclosing_context,none,
8143-
"make %kindbase0 @safe(unchecked) to allow it to use unsafe constructs in its definition",
8144-
(const Decl *))
8145-
NOTE(make_enclosing_context_unsafe,none,
8146-
"make %kindbase0 @unsafe to indicate that its use is not memory-safe",
8147-
(const Decl *))
8124+
GROUPED_WARNING(unsafe_without_unsafe,Unsafe,none,
8125+
"expression uses unsafe constructs but is not marked with 'unsafe'", ())
8126+
WARNING(no_unsafe_in_unsafe,none,
8127+
"no unsafe operations occur within 'unsafe' expression", ())
81488128
NOTE(make_subclass_unsafe,none,
81498129
"make class %0 @unsafe to allow unsafe overrides of safe superclass methods",
81508130
(DeclName))
8151-
NOTE(make_conforming_context_unsafe,none,
8152-
"make the enclosing %0 @unsafe to allow unsafe conformance to protocol %1",
8153-
(DescriptiveDeclKind, DeclName))
81548131

81558132
//===----------------------------------------------------------------------===//
81568133
// MARK: Value Generics

include/swift/AST/Effects.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ class ProtocolDecl;
4040

4141
enum class EffectKind : uint8_t {
4242
Throws = 1 << 0,
43-
Async = 1 << 1
43+
Async = 1 << 1,
44+
Unsafe = 1 << 2,
4445
};
4546
using PossibleEffects = OptionSet<EffectKind>;
4647

include/swift/AST/Expr.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2173,6 +2173,36 @@ class AwaitExpr final : public IdentityExpr {
21732173
}
21742174
};
21752175

2176+
/// UnsafeExpr - An 'unsafe' surrounding an expression, marking that the
2177+
/// expression contains uses of unsafe declarations.
2178+
///
2179+
/// getSemanticsProvidingExpr() looks through this because it doesn't
2180+
/// provide the value and only very specific clients care where the
2181+
/// 'unsafe' was written.
2182+
class UnsafeExpr final : public IdentityExpr {
2183+
SourceLoc UnsafeLoc;
2184+
public:
2185+
UnsafeExpr(SourceLoc unsafeLoc, Expr *sub, Type type = Type(),
2186+
bool implicit = false)
2187+
: IdentityExpr(ExprKind::Unsafe, sub, type, implicit),
2188+
UnsafeLoc(unsafeLoc) {
2189+
}
2190+
2191+
static UnsafeExpr *createImplicit(ASTContext &ctx, SourceLoc unsafeLoc, Expr *sub, Type type = Type()) {
2192+
return new (ctx) UnsafeExpr(unsafeLoc, sub, type, /*implicit=*/true);
2193+
}
2194+
2195+
SourceLoc getLoc() const { return UnsafeLoc; }
2196+
2197+
SourceLoc getUnsafeLoc() const { return UnsafeLoc; }
2198+
SourceLoc getStartLoc() const { return UnsafeLoc; }
2199+
SourceLoc getEndLoc() const { return getSubExpr()->getEndLoc(); }
2200+
2201+
static bool classof(const Expr *e) {
2202+
return e->getKind() == ExprKind::Unsafe;
2203+
}
2204+
};
2205+
21762206
/// ConsumeExpr - A 'consume' surrounding an lvalue expression marking the
21772207
/// lvalue as needing to be moved.
21782208
class ConsumeExpr final : public Expr {

include/swift/AST/ExprNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ ABSTRACT_EXPR(Identity, Expr)
107107
EXPR(Paren, IdentityExpr)
108108
EXPR(DotSelf, IdentityExpr)
109109
EXPR(Await, IdentityExpr)
110+
EXPR(Unsafe, IdentityExpr)
110111
EXPR(Borrow, IdentityExpr)
111112
EXPR(UnresolvedMemberChainResult, IdentityExpr)
112113
EXPR_RANGE(Identity, Paren, UnresolvedMemberChainResult)

include/swift/AST/SourceFileExtras.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#ifndef SWIFT_AST_SOURCEFILEEXTRAS_H
1414
#define SWIFT_AST_SOURCEFILEEXTRAS_H
1515

16-
#include "swift/AST/UnsafeUse.h"
1716
#include "llvm/ADT/DenseMap.h"
1817
#include <vector>
1918

@@ -24,11 +23,6 @@ class Decl;
2423
/// Extra information associated with a source file that is lazily created and
2524
/// stored in a separately-allocated side structure.
2625
struct SourceFileExtras {
27-
/// Captures all of the unsafe uses associated with a given declaration.
28-
///
29-
/// The declaration is the entity that can be annotated (e.g., with @unsafe)
30-
/// to suppress all of the unsafe-related diagnostics listed here.
31-
llvm::DenseMap<const Decl *, std::vector<UnsafeUse>> unsafeUses;
3226
};
3327

3428
}

include/swift/AST/UnsafeUse.h

Lines changed: 9 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,11 @@ class UnsafeUse {
7474
struct {
7575
TypeBase *type;
7676
void *conformanceRef;
77-
DeclContext *declContext;
7877
const void *location;
7978
} conformance;
8079

8180
struct {
8281
const Decl *decl;
83-
DeclContext *declContext;
8482
TypeBase *type;
8583
const void *location;
8684
} entity;
@@ -107,7 +105,6 @@ class UnsafeUse {
107105

108106
static UnsafeUse forReference(
109107
Kind kind,
110-
DeclContext *dc,
111108
const Decl *decl,
112109
Type type,
113110
SourceLoc location
@@ -121,7 +118,6 @@ class UnsafeUse {
121118

122119
UnsafeUse result(kind);
123120
result.storage.entity.decl = decl;
124-
result.storage.entity.declContext = dc;
125121
result.storage.entity.type = type.getPointer();
126122
result.storage.entity.location = location.getOpaquePointerValue();
127123
return result;
@@ -152,49 +148,41 @@ class UnsafeUse {
152148

153149
static UnsafeUse forConformance(Type subjectType,
154150
ProtocolConformanceRef conformance,
155-
SourceLoc location,
156-
DeclContext *dc) {
151+
SourceLoc location) {
157152
assert(subjectType);
158153
UnsafeUse result(UnsafeConformance);
159154
result.storage.conformance.type = subjectType.getPointer();
160155
result.storage.conformance.conformanceRef = conformance.getOpaqueValue();
161-
result.storage.conformance.declContext = dc;
162156
result.storage.conformance.location = location.getOpaquePointerValue();
163157
return result;
164158
}
165159

166-
static UnsafeUse forUnownedUnsafe(const Decl *decl,
167-
SourceLoc location,
168-
DeclContext *dc) {
169-
return forReference(UnownedUnsafe, dc, decl, Type(), location);
160+
static UnsafeUse forUnownedUnsafe(const Decl *decl, SourceLoc location) {
161+
return forReference(UnownedUnsafe, decl, Type(), location);
170162
}
171163

172164
static UnsafeUse forExclusivityUnchecked(const Decl *decl,
173-
SourceLoc location,
174-
DeclContext *dc) {
175-
return forReference(ExclusivityUnchecked, dc, decl, Type(), location);
165+
SourceLoc location) {
166+
return forReference(ExclusivityUnchecked, decl, Type(), location);
176167
}
177168

178169
static UnsafeUse forNonisolatedUnsafe(const Decl *decl,
179-
SourceLoc location,
180-
DeclContext *dc) {
181-
return forReference(NonisolatedUnsafe, dc, decl, Type(), location);
170+
SourceLoc location) {
171+
return forReference(NonisolatedUnsafe, decl, Type(), location);
182172
}
183173

184174
static UnsafeUse forReferenceToUnsafe(const Decl *decl,
185175
bool isCall,
186-
DeclContext *dc,
187176
Type type,
188177
SourceLoc location) {
189-
return forReference(isCall ? CallToUnsafe: ReferenceToUnsafe, dc,
178+
return forReference(isCall ? CallToUnsafe: ReferenceToUnsafe,
190179
decl, type, location);
191180
}
192181

193182
static UnsafeUse forReferenceToUnsafeThroughTypealias(const Decl *decl,
194-
DeclContext *dc,
195183
Type type,
196184
SourceLoc location) {
197-
return forReference(ReferenceToUnsafeThroughTypealias, dc,
185+
return forReference(ReferenceToUnsafeThroughTypealias,
198186
decl, type, location);
199187
}
200188

@@ -269,32 +257,6 @@ class UnsafeUse {
269257
return storage.typeWitness.assocType;
270258
}
271259

272-
/// Retrieve the declaration context in which the reference occurs.
273-
DeclContext *getDeclContext() const {
274-
switch (getKind()) {
275-
case UnownedUnsafe:
276-
case ExclusivityUnchecked:
277-
case NonisolatedUnsafe:
278-
case ReferenceToUnsafe:
279-
case ReferenceToUnsafeThroughTypealias:
280-
case CallToUnsafe:
281-
return storage.entity.declContext;
282-
283-
case Override:
284-
return getDecl()->getDeclContext();
285-
286-
case Witness:
287-
case TypeWitness:
288-
return getConformance().getConcrete()->getDeclContext();
289-
290-
case UnsafeConformance:
291-
return storage.conformance.declContext;
292-
293-
case PreconcurrencyImport:
294-
return storage.importDecl->getDeclContext();
295-
}
296-
}
297-
298260
/// Get the original declaration for an issue with a polymorphic
299261
/// implementation, e.g., an overridden declaration or a protocol
300262
/// requirement.

lib/AST/ASTDumper.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2528,6 +2528,11 @@ class PrintExpr : public ExprVisitor<PrintExpr, void, StringRef>,
25282528
printRec(E->getSubExpr());
25292529
printFoot();
25302530
}
2531+
void visitUnsafeExpr(UnsafeExpr *E, StringRef label) {
2532+
printCommon(E, "unsafe_expr", label);
2533+
printRec(E->getSubExpr());
2534+
printFoot();
2535+
}
25312536
void visitConsumeExpr(ConsumeExpr *E, StringRef label) {
25322537
printCommon(E, "consume_expr", label);
25332538
printRec(E->getSubExpr());

lib/AST/ASTPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4817,6 +4817,11 @@ void PrintAST::visitAwaitExpr(AwaitExpr *expr) {
48174817
visit(expr->getSubExpr());
48184818
}
48194819

4820+
void PrintAST::visitUnsafeExpr(UnsafeExpr *expr) {
4821+
Printer << "unsafe ";
4822+
visit(expr->getSubExpr());
4823+
}
4824+
48204825
void PrintAST::visitConsumeExpr(ConsumeExpr *expr) {
48214826
Printer << "consume ";
48224827
visit(expr->getSubExpr());

0 commit comments

Comments
 (0)