Skip to content

Commit 268d5cc

Browse files
committed
Suppress strict safety diagnostics in @unsafe declarations
When a declaration is `@unsafe`, don't emit strict safety diagnostics for uses of unsafe entities, constructs, or types within it. This allows one to account for all unsafe behavior in a module using strict memory safety by marking the appropriate declarations `@unsafe`. Enhance the strict-safety diagnostics to suggest the addition of `@unsafe` where it is needed to suppress them, with a Fix-It. Ensure that all such diagnostics can be suppressed via `@unsafe` so it's possible to get to the above state. Also includes a drive-by bug fix where we weren't diagnosing unsafe methods overriding safe ones in some cases. Fixes rdar://139467327.
1 parent 8553f99 commit 268d5cc

22 files changed

+282
-27
lines changed

include/swift/AST/AvailabilityContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ class AvailabilityContext {
8686
/// Returns true if this context is `@_unavailableInEmbedded`.
8787
bool isUnavailableInEmbedded() const;
8888

89+
/// Returns true if this context allows the use of unsafe constructs inside
90+
/// it.
91+
bool allowsUnsafe() const;
92+
8993
/// Constrain with another `AvailabilityContext`.
9094
void constrainWithContext(const AvailabilityContext &other, ASTContext &ctx);
9195

include/swift/AST/AvailabilityContextStorage.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ struct AvailabilityContext::PlatformInfo {
4343
/// platform.
4444
unsigned IsDeprecated : 1;
4545

46+
/// Whether or not the context allows unsafe code within it, e.g., via the
47+
/// `@unsafe` attribute.
48+
unsigned AllowsUnsafe: 1;
49+
4650
/// Sets each field to the value of the corresponding field in `other` if the
4751
/// other is more restrictive. Returns true if any field changed as a result
4852
/// of adding this constraint.
@@ -63,6 +67,7 @@ struct AvailabilityContext::PlatformInfo {
6367
ID.AddBoolean(IsUnavailableInEmbedded);
6468
ID.AddInteger(static_cast<uint8_t>(UnavailablePlatform));
6569
ID.AddBoolean(IsDeprecated);
70+
ID.AddBoolean(AllowsUnsafe);
6671
}
6772
};
6873

include/swift/AST/Decl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
11911191
/// used in a "safe" dialect.
11921192
bool isUnsafe() const;
11931193

1194+
/// Whether this declaration explicitly states that it is allowed to contain
1195+
/// unsafe code.
1196+
bool allowsUnsafe() const;
1197+
11941198
private:
11951199
bool isUnsafeComputed() const {
11961200
return Bits.Decl.IsUnsafeComputed;
@@ -1908,6 +1912,7 @@ class ExtensionDecl final : public GenericContext, public Decl,
19081912
friend class Decl;
19091913
public:
19101914
using Decl::getASTContext;
1915+
using Decl::allowsUnsafe;
19111916

19121917
/// Create a new extension declaration.
19131918
static ExtensionDecl *create(ASTContext &ctx, SourceLoc extensionLoc,
@@ -3380,6 +3385,7 @@ class GenericTypeDecl : public GenericContext, public TypeDecl {
33803385
using DeclContext::operator new;
33813386
using DeclContext::operator delete;
33823387
using TypeDecl::getDeclaredInterfaceType;
3388+
using Decl::allowsUnsafe;
33833389

33843390
static bool classof(const DeclContext *C) {
33853391
if (auto D = C->getAsDecl())

include/swift/AST/DeclAttr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ SIMPLE_DECL_ATTR(sensitive, Sensitive,
504504
159)
505505

506506
SIMPLE_DECL_ATTR(unsafe, Unsafe,
507-
OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType |
507+
OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType | OnExtension |
508508
UserInaccessible |
509509
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
510510
160)

include/swift/AST/DeclContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,10 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
713713
/// target. Used for conformance lookup disambiguation.
714714
bool isAlwaysAvailableConformanceContext() const;
715715

716+
/// Determines whether this context is explicitly allowed to use unsafe
717+
/// constructs.
718+
bool allowsUnsafe() const;
719+
716720
/// \returns true if traversal was aborted, false otherwise.
717721
bool walkContext(ASTWalker &Walker);
718722

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8091,6 +8091,15 @@ GROUPED_WARNING(reference_to_unsafe_typed_decl,Unsafe,none,
80918091
(bool, const ValueDecl *, Type))
80928092
NOTE(unsafe_decl_here,none,
80938093
"unsafe %kindbase0 declared here", (const ValueDecl *))
8094+
NOTE(make_enclosing_context_unsafe,none,
8095+
"mark the enclosing %kindbase0 '@unsafe' to allow it to use unsafe constructs",
8096+
(const Decl *))
8097+
NOTE(make_subclass_unsafe,none,
8098+
"mark the class %0 '@unsafe' to allow unsafe overrides of safe superclass methods",
8099+
(DeclName))
8100+
NOTE(make_conforming_context_unsafe,none,
8101+
"mark the enclosing %0 with '@unsafe' to allow unsafe conformance to protocol %1",
8102+
(DescriptiveDeclKind, DeclName))
80948103

80958104
//===----------------------------------------------------------------------===//
80968105
// MARK: Value Generics

lib/AST/AvailabilityContext.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ bool AvailabilityContext::PlatformInfo::constrainWith(
4646
CONSTRAIN_BOOL(IsUnavailableInEmbedded, other.IsUnavailableInEmbedded);
4747
}
4848
isConstrained |= CONSTRAIN_BOOL(IsDeprecated, other.IsDeprecated);
49+
isConstrained |= CONSTRAIN_BOOL(AllowsUnsafe, other.AllowsUnsafe);
4950

5051
return isConstrained;
5152
}
@@ -63,6 +64,7 @@ bool AvailabilityContext::PlatformInfo::constrainWith(const Decl *decl) {
6364
}
6465

6566
isConstrained |= CONSTRAIN_BOOL(IsDeprecated, decl->isDeprecated());
67+
isConstrained |= CONSTRAIN_BOOL(AllowsUnsafe, decl->allowsUnsafe());
6668

6769
return isConstrained;
6870
}
@@ -128,7 +130,8 @@ AvailabilityContext::forPlatformRange(const AvailabilityRange &range,
128130
PlatformInfo platformInfo{range, PlatformKind::none,
129131
/*IsUnavailable*/ false,
130132
/*IsUnavailableInEmbedded*/ false,
131-
/*IsDeprecated*/ false};
133+
/*IsDeprecated*/ false,
134+
/*AllowsUnsafe*/ false};
132135
return AvailabilityContext(Storage::get(platformInfo, ctx));
133136
}
134137

@@ -151,7 +154,8 @@ AvailabilityContext::get(const AvailabilityRange &platformAvailability,
151154
? *unavailablePlatform
152155
: PlatformKind::none,
153156
unavailablePlatform.has_value(),
154-
/*IsUnavailableInEmbedded*/ false, deprecated};
157+
/*IsUnavailableInEmbedded*/ false, deprecated,
158+
/*AllowsUnsafe*/ false};
155159
return AvailabilityContext(Storage::get(platformInfo, ctx));
156160
}
157161

@@ -170,6 +174,10 @@ bool AvailabilityContext::isUnavailableInEmbedded() const {
170174
return Info->Platform.IsUnavailableInEmbedded;
171175
}
172176

177+
bool AvailabilityContext::allowsUnsafe() const {
178+
return Info->Platform.AllowsUnsafe;
179+
}
180+
173181
bool AvailabilityContext::isDeprecated() const {
174182
return Info->Platform.IsDeprecated;
175183
}

lib/AST/Decl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,10 @@ bool Decl::isUnsafe() const {
10791079
false);
10801080
}
10811081

1082+
bool Decl::allowsUnsafe() const {
1083+
return isUnsafe();
1084+
}
1085+
10821086
Type AbstractFunctionDecl::getThrownInterfaceType() const {
10831087
if (!getThrownTypeRepr())
10841088
return ThrownType.getType();

lib/AST/DeclContext.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,3 +1549,10 @@ bool DeclContext::isAlwaysAvailableConformanceContext() const {
15491549

15501550
return deploymentTarget.isContainedIn(conformanceAvailability);
15511551
}
1552+
1553+
bool DeclContext::allowsUnsafe() const {
1554+
if (auto decl = getAsDecl())
1555+
return decl->allowsUnsafe();
1556+
1557+
return false;
1558+
}

lib/Sema/AssociatedTypeInference.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,11 @@ static void recordTypeWitness(NormalProtocolConformance *conformance,
366366
diagnoseUnsafeType(ctx,
367367
loc,
368368
type,
369+
conformance->getDeclContext(),
369370
[&](Type specificType) {
370371
ctx.Diags.diagnose(
371372
loc, diag::type_witness_unsafe, specificType, assocType->getName());
373+
suggestUnsafeMarkerOnConformance(conformance);
372374
assocType->diagnose(diag::decl_declared_here, assocType);
373375
});
374376
}

0 commit comments

Comments
 (0)