Skip to content

Commit 4b3c18d

Browse files
committed
Account for implicit unsafety in unsafe witness and override checks
When a witness is unsafe and its corresponding requirement is not unsafe, the conformance itself needs to be determined to be unsafe. When doing this check, account for the fact that the requirement might be implicitly unsafe, i.e., it uses unsafe types. In such cases, the requirement is effectively unsafe, so the unsafe witness to it does not make the conformance unsafe. Therefore, suppress the diagnostic. This accounts for cases where the protocol comes from a module that didn't enable strict memory safety checking, or didn't suppress all warnings related to it, as well as cases where substitution of type witnesses introduces the unsafe type. The same thing occurs with overriding a method: an unsafe override of a method that involves unsafe types (but was not marked @unsafe for whatever reason) does not make the subclassing itself unsafe, so don't diagnose it as such.
1 parent ba23f36 commit 4b3c18d

File tree

7 files changed

+61
-10
lines changed

7 files changed

+61
-10
lines changed

lib/Sema/TypeCheckAvailability.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,10 @@ void diagnoseUnsafeUse(const UnsafeUse &use, bool asNote = false);
280280
/// declaration, if there are any.
281281
void diagnoseUnsafeUsesIn(const Decl *decl);
282282

283+
/// Determine whether a reference to this declaration is considered unsafe,
284+
/// either explicitly (@unsafe) or because it references an unsafe type.
285+
bool isUnsafe(ConcreteDeclRef declRef);
286+
283287
} // namespace swift
284288

285289
#endif // SWIFT_SEMA_TYPE_CHECK_AVAILABILITY_H

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2249,14 +2249,20 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
22492249
diagnoseOverrideForAvailability(override, base);
22502250
}
22512251

2252-
if (ctx.LangOpts.hasFeature(Feature::WarnUnsafe) &&
2253-
override->isUnsafe() && !base->isUnsafe()) {
2254-
// Don't diagnose @unsafe overrides if the subclass is @unsafe.
2255-
auto overridingClass = override->getDeclContext()->getSelfClassDecl();
2256-
bool shouldDiagnose = !overridingClass || !overridingClass->allowsUnsafe();
2257-
2258-
if (shouldDiagnose) {
2259-
diagnoseUnsafeUse(UnsafeUse::forOverride(override, base));
2252+
if (ctx.LangOpts.hasFeature(Feature::WarnUnsafe)) {
2253+
// If the override is unsafe but the base declaration is not, then the
2254+
// inheritance itself is unsafe.
2255+
auto subs = SubstitutionMap::getOverrideSubstitutions(base, override);
2256+
ConcreteDeclRef overrideRef(override);
2257+
ConcreteDeclRef baseRef(base, subs);
2258+
if (isUnsafe(overrideRef) && !isUnsafe(baseRef)) {
2259+
// Don't diagnose @unsafe overrides if the subclass is @unsafe.
2260+
auto overridingClass = override->getDeclContext()->getSelfClassDecl();
2261+
bool shouldDiagnose = !overridingClass || !overridingClass->allowsUnsafe();
2262+
2263+
if (shouldDiagnose) {
2264+
diagnoseUnsafeUse(UnsafeUse::forOverride(override, base));
2265+
}
22602266
}
22612267
}
22622268
/// Check attributes associated with the base; some may need to merged with

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2577,8 +2577,14 @@ checkIndividualConformance(NormalProtocolConformance *conformance) {
25772577
if (!valueRequirement)
25782578
continue;
25792579

2580+
// If the witness is unsafe and the requirement is not effectively
2581+
// unsafe, then the conformance must be unsafe.
25802582
if (auto witness = conformance->getWitnessUncached(valueRequirement)) {
2581-
if (witness.getDecl()->isUnsafe() && !requirement->isUnsafe()) {
2583+
if (isUnsafe(witness.getDeclRef()) &&
2584+
!isUnsafe(
2585+
ConcreteDeclRef(
2586+
valueRequirement,
2587+
witness.getRequirementToWitnessThunkSubs()))) {
25822588
unsafeUses.push_back(
25832589
UnsafeUse::forWitness(
25842590
witness.getDecl(), requirement, conformance));

lib/Sema/TypeCheckUnsafe.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,21 @@ void swift::diagnoseUnsafeUsesIn(const Decl *decl) {
280280
diagnoseUnsafeUse(unsafeUse, /*asNote=*/true);
281281
}
282282
}
283+
284+
bool swift::isUnsafe(ConcreteDeclRef declRef) {
285+
auto decl = declRef.getDecl();
286+
if (!decl)
287+
return false;
288+
289+
// Is the declaration explicitly @unsafe?
290+
if (decl->isUnsafe())
291+
return true;
292+
293+
auto type = decl->getInterfaceType();
294+
if (auto subs = declRef.getSubstitutions())
295+
type = type.subst(subs);
296+
if (type->isUnsafe())
297+
return true;
298+
299+
return false;
300+
}

test/Unsafe/Inputs/unsafe_swift_decls.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,11 @@ public protocol Ptrable {
1313
}
1414

1515
extension HasAPointerType: Ptrable { }
16+
17+
public protocol HasUnsafeRequirement {
18+
func f(_: PointerType)
19+
}
20+
21+
open class SuperclassWithUnsafeMethod {
22+
open func implicitlyUnsafe(_: PointerType) { }
23+
}

test/Unsafe/unsafe.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct ConformsToMultiP { }
5050
// expected-warning@+1{{conformance of 'ConformsToMultiP' to protocol 'MultiP' involves unsafe code; use '@unsafe' to indicate that the conformance is not memory-safe [Unsafe]}}{{29-29=@unsafe }}
5151
extension ConformsToMultiP: MultiP {
5252
// expected-note@-1{{unsafe type 'UnsafeSuper' cannot satisfy safe associated type 'Ptr'}}
53-
@unsafe func f() -> UnsafeSuper { .init() } // expected-note{{unsafe instance method 'f()' cannot satisfy safe requirement}}
53+
@unsafe func f() -> UnsafeSuper { .init() }
5454
}
5555

5656
// -----------------------------------------------------------------------

test/Unsafe/unsafe_imports.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,12 @@ func testUnsafe(_ ut: UnsafeType) { // expected-note{{reference to unsafe struct
2424
// expected-warning@+1{{global function 'testUnsafeThroughAlias' involves unsafe code; use '@unsafe' to indicate that its use is not memory-safe}}
2525
func testUnsafeThroughAlias(_ ut: UnsafeTypeAlias) { // expected-note{{reference to type alias 'UnsafeTypeAlias' whose underlying type involves unsafe type 'PointerType'}}
2626
}
27+
28+
29+
struct ConformsToUnsafeRequirement: HasUnsafeRequirement {
30+
@unsafe func f(_: PointerType) { }
31+
}
32+
33+
class SubclassWithUnsafeMethod: SuperclassWithUnsafeMethod {
34+
@unsafe override func implicitlyUnsafe(_: PointerType) { }
35+
}

0 commit comments

Comments
 (0)