Always emit "unsafe does not cover any unsafe constructs" warning

Check for unsafe constructs in all modes, so that we can emit the
"unsafe does not cover any unsafe constructs" warning consistently.
One does not need to write "unsafe" outside of strict memory safety
mode, but if you do... it needs to cover unsafe behavior.

(cherry picked from commit 1b94c3b3d6)
This commit is contained in:
Doug Gregor
2025-05-29 15:10:43 -07:00
parent 589fd330e0
commit 60024c3c3b
7 changed files with 9 additions and 23 deletions

View File

@@ -8172,7 +8172,7 @@ Expr *ExprRewriter::convertLiteralInPlace(
Diag<> brokenProtocolDiag, Diag<> brokenBuiltinProtocolDiag) {
// If coercing a literal to an unresolved type, we don't try to look up the
// witness members, just do it.
if (type->is<UnresolvedType>()) {
if (type->is<UnresolvedType>() || type->is<ErrorType>()) {
cs.setType(literal, type);
return literal;
}

View File

@@ -1187,8 +1187,7 @@ public:
Classification result;
bool considerAsync = !onlyEffect || *onlyEffect == EffectKind::Async;
bool considerThrows = !onlyEffect || *onlyEffect == EffectKind::Throws;
bool considerUnsafe = (!onlyEffect || *onlyEffect == EffectKind::Unsafe) &&
ctx.LangOpts.hasFeature(Feature::StrictMemorySafety);
bool considerUnsafe = (!onlyEffect || *onlyEffect == EffectKind::Unsafe);
// If we're tracking "unsafe" effects, compute them here.
if (considerUnsafe) {
@@ -1710,8 +1709,7 @@ public:
!fnType->isAsync() &&
!E->isImplicitlyAsync() &&
!hasAnyConformances &&
(fnRef.getExplicitSafety() == ExplicitSafety::Safe ||
!ctx.LangOpts.hasFeature(Feature::StrictMemorySafety))) {
fnRef.getExplicitSafety() == ExplicitSafety::Safe) {
return Classification();
}
@@ -1932,7 +1930,6 @@ public:
// If the safety of the callee is unspecified, check the safety of the
// arguments specifically.
if (hasUnspecifiedSafety &&
ctx.LangOpts.hasFeature(Feature::StrictMemorySafety) &&
!(assumedSafeArguments && assumedSafeArguments->contains(E))) {
classifyApplyEffect(EffectKind::Unsafe);
}
@@ -4559,8 +4556,7 @@ private:
Ctx.Diags.diagnose(S->getForLoc(), diag::for_unsafe_without_unsafe)
.fixItInsert(insertionLoc, "unsafe ");
}
} else if (S->getUnsafeLoc().isValid() &&
Ctx.LangOpts.hasFeature(Feature::StrictMemorySafety)) {
} else if (S->getUnsafeLoc().isValid()) {
// Extraneous "unsafe" on the sequence.
Ctx.Diags.diagnose(S->getUnsafeLoc(), diag::no_unsafe_in_unsafe_for)
.fixItRemove(S->getUnsafeLoc());
@@ -4605,9 +4601,6 @@ private:
}
void diagnoseRedundantUnsafe(UnsafeExpr *E) const {
if (!Ctx.LangOpts.hasFeature(Feature::StrictMemorySafety))
return;
// Silence this warning in the expansion of the _SwiftifyImport macro.
// This is a hack because it's tricky to determine when to insert "unsafe".
unsigned bufferID =

View File

@@ -341,10 +341,6 @@ bool swift::enumerateUnsafeUses(ArrayRef<ProtocolConformanceRef> conformances,
if (conformance.isInvalid())
continue;
ASTContext &ctx = conformance.getProtocol()->getASTContext();
if (!ctx.LangOpts.hasFeature(Feature::StrictMemorySafety))
return false;
if (!conformance.hasEffect(EffectKind::Unsafe))
continue;
@@ -413,9 +409,6 @@ bool swift::isUnsafeInConformance(const ValueDecl *requirement,
void swift::diagnoseUnsafeType(ASTContext &ctx, SourceLoc loc, Type type,
llvm::function_ref<void(Type)> diagnose) {
if (!ctx.LangOpts.hasFeature(Feature::StrictMemorySafety))
return;
if (!type->isUnsafe())
return;

View File

@@ -4,7 +4,7 @@
func baz(y: [Int], z: Int) -> Int {
switch z {
case y[let z]: // expected-error {{'let' binding pattern cannot appear in an expression}}
case y[let z]: // expected-error 2{{'let' binding pattern cannot appear in an expression}}
z
default:
z

View File

@@ -406,7 +406,7 @@ func testNonBinding5(_ x: Int, y: [Int]) {
func testNonBinding6(y: [Int], z: Int) -> Int {
switch 0 {
// We treat 'z' here as a binding, which is invalid.
case let y[z]: // expected-error {{pattern variable binding cannot appear in an expression}}
case let y[z]: // expected-error 2{{pattern variable binding cannot appear in an expression}}
z
case y[z]: // This is fine
0

View File

@@ -14,5 +14,5 @@ func acceptP<T: P>(_: T) { }
func testItAll(ut: UnsafeType, x: X, i: Int) {
_ = unsafe ut
unsafe acceptP(x)
_ = unsafe i
_ = unsafe i // expected-warning{{no unsafe operations occur within 'unsafe' expression}}
}

View File

@@ -361,11 +361,11 @@ func testSequenceExpr() async throws(Never) {
_ = unsafe await try! getIntAsync() + getIntAsync()
// expected-warning@-1 {{'try' must precede 'await'}}
// expected-warning@-2{{no unsafe operations occur within 'unsafe' expression}}
_ = try unsafe await try! getIntAsync() + getIntAsync()
// expected-warning@-1 {{'try' must precede 'await'}}
// expected-warning@-2 {{no calls to throwing functions occur within 'try' expression}}
// expected-warning@-3{{no unsafe operations occur within 'unsafe' expression}}
try _ = (try! getInt()) + getInt()
// expected-error@-1:29 {{thrown expression type 'any Error' cannot be converted to error type 'Never'}}