Skip to content

Commit d6c020d

Browse files
committed
Diagnose 'unsafe' within explicit references to types in expressions
1 parent 4464744 commit d6c020d

File tree

7 files changed

+95
-33
lines changed

7 files changed

+95
-33
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8093,6 +8093,9 @@ NOTE(note_reference_to_unsafe_typed_decl,none,
80938093
NOTE(note_reference_to_unsafe_through_typealias,none,
80948094
"reference to %kind0 whose underlying type involves unsafe type %1",
80958095
(const ValueDecl *, Type))
8096+
NOTE(note_reference_to_unsafe_type,none,
8097+
"reference to unsafe type %0",
8098+
(Type))
80968099
NOTE(note_reference_to_nonisolated_unsafe,none,
80978100
"reference to nonisolated(unsafe) %kind0 is unsafe in concurrently-executing code",
80988101
(const ValueDecl *))

lib/Sema/TypeCheckEffects.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,12 @@ class EffectsHandlingWalker : public ASTWalker {
607607
recurse = asImpl().checkDeclRef(CE, CE->getInitializer(), CE->getLoc(),
608608
/*isImplicitlyAsync=*/false,
609609
/*isImplicitlyThrows=*/false);
610+
} else if (auto ECE = dyn_cast<ExplicitCastExpr>(E)) {
611+
recurse = asImpl().checkType(E, ECE->getCastTypeRepr(), ECE->getCastType());
612+
} else if (auto TE = dyn_cast<TypeExpr>(E)) {
613+
if (!TE->isImplicit()) {
614+
recurse = asImpl().checkType(TE, TE->getTypeRepr(), TE->getInstanceType());
615+
}
610616
}
611617
// Error handling validation (via checkTopLevelEffects) happens after
612618
// type checking. If an unchecked expression is still around, the code was
@@ -1139,7 +1145,7 @@ class Classification {
11391145
return result;
11401146
}
11411147

1142-
/// Used when all we have is are conformances. This can only find
1148+
/// Used when all we have are conformances. This can only find
11431149
/// "unsafe" uses.
11441150
static Classification forConformances(
11451151
ArrayRef<ProtocolConformanceRef> conformances,
@@ -1153,6 +1159,18 @@ class Classification {
11531159
return result;
11541160
}
11551161

1162+
/// Used when all we have are conformances. This can only find
1163+
/// "unsafe" uses.
1164+
static Classification forType(Type type, SourceLoc loc) {
1165+
Classification result;
1166+
diagnoseUnsafeType(type->getASTContext(), loc, type, [&](Type unsafeType) {
1167+
result.recordUnsafeUse(
1168+
UnsafeUse::forReferenceToUnsafe(
1169+
nullptr, /*isCall=*/false, unsafeType, loc));
1170+
});
1171+
return result;
1172+
}
1173+
11561174
void merge(Classification other) {
11571175
if (other.isInvalid())
11581176
IsInvalid = true;
@@ -1927,6 +1945,10 @@ class ApplyClassifier {
19271945
return ShouldRecurse;
19281946
}
19291947

1948+
ShouldRecurse_t checkType(Expr *E, TypeRepr *typeRepr, Type type) {
1949+
return ShouldRecurse;
1950+
}
1951+
19301952
ConditionalEffectKind checkExhaustiveDoBody(DoCatchStmt *S) {
19311953
// All errors thrown by the do body are caught, but any errors thrown
19321954
// by the catch bodies are bounded by the throwing kind of the do body.
@@ -2061,6 +2083,10 @@ class ApplyClassifier {
20612083
return ShouldRecurse;
20622084
}
20632085

2086+
ShouldRecurse_t checkType(Expr *E, TypeRepr *typeRepr, Type type) {
2087+
return ShouldRecurse;
2088+
}
2089+
20642090
void visitExprPre(Expr *expr) { return; }
20652091
};
20662092

@@ -3485,6 +3511,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
34853511
return ShouldRecurse;
34863512
}
34873513

3514+
ShouldRecurse_t checkType(Expr *E, TypeRepr *typeRepr, Type type) {
3515+
SourceLoc loc = typeRepr ? typeRepr->getLoc() : E->getLoc();
3516+
auto classification = Classification::forType(type, loc);
3517+
checkEffectSite(E, /*requiresTry=*/false, classification);
3518+
return ShouldRecurse;
3519+
}
3520+
34883521
ConditionalEffectKind checkExhaustiveDoBody(DoCatchStmt *S) {
34893522
// This is a context where errors are handled.
34903523
ContextScope scope(*this, CurContext.withHandlesErrors());

lib/Sema/TypeCheckType.cpp

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6672,27 +6672,3 @@ Type ExplicitCaughtTypeRequest::evaluate(
66726672

66736673
llvm_unreachable("Unhandled catch node");
66746674
}
6675-
6676-
void swift::diagnoseUnsafeType(ASTContext &ctx, SourceLoc loc, Type type,
6677-
llvm::function_ref<void(Type)> diagnose) {
6678-
if (!ctx.LangOpts.hasFeature(Feature::WarnUnsafe))
6679-
return;
6680-
6681-
if (!type->isUnsafe() && !type->getCanonicalType()->isUnsafe())
6682-
return;
6683-
6684-
// Look for a specific @unsafe nominal type.
6685-
Type specificType;
6686-
type.findIf([&specificType](Type type) {
6687-
if (auto typeDecl = type->getAnyNominal()) {
6688-
if (typeDecl->isUnsafe()) {
6689-
specificType = type;
6690-
return false;
6691-
}
6692-
}
6693-
6694-
return false;
6695-
});
6696-
6697-
diagnose(specificType ? specificType : type);
6698-
}

lib/Sema/TypeCheckType.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -748,11 +748,6 @@ bool diagnoseMissingOwnership(ParamSpecifier ownership,
748748
TypeRepr *repr, Type ty,
749749
const TypeResolution &resolution);
750750

751-
/// If the given type involves an unsafe type, diagnose it by calling the
752-
/// diagnose function with the most specific unsafe type that can be provided.
753-
void diagnoseUnsafeType(ASTContext &ctx, SourceLoc loc, Type type,
754-
llvm::function_ref<void(Type)> diagnose);
755-
756751
} // end namespace swift
757752

758753
#endif /* SWIFT_SEMA_TYPE_CHECK_TYPE_H */

lib/Sema/TypeCheckUnsafe.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,11 @@ void swift::diagnoseUnsafeUse(const UnsafeUse &use) {
123123
case UnsafeUse::ReferenceToUnsafe:
124124
case UnsafeUse::CallToUnsafe: {
125125
bool isCall = use.getKind() == UnsafeUse::CallToUnsafe;
126-
auto decl = cast<ValueDecl>(use.getDecl());
126+
auto decl = cast_or_null<ValueDecl>(use.getDecl());
127127
auto loc = use.getLocation();
128128
Type type = use.getType();
129-
ASTContext &ctx = decl->getASTContext();
130-
if (type) {
129+
ASTContext &ctx = decl ? decl->getASTContext() : type->getASTContext();
130+
if (type && decl) {
131131
diagnoseUnsafeType(
132132
ctx, loc, type,
133133
[&](Type specificType) {
@@ -136,6 +136,11 @@ void swift::diagnoseUnsafeUse(const UnsafeUse &use) {
136136
diag::note_reference_to_unsafe_typed_decl,
137137
isCall, decl, specificType);
138138
});
139+
} else if (type) {
140+
ctx.Diags.diagnose(
141+
loc,
142+
diag::note_reference_to_unsafe_type,
143+
type);
139144
} else {
140145
ctx.Diags.diagnose(
141146
loc,
@@ -336,3 +341,27 @@ bool swift::isUnsafeInConformance(const ValueDecl *requirement,
336341
});
337342
return hasUnsafeType;
338343
}
344+
345+
void swift::diagnoseUnsafeType(ASTContext &ctx, SourceLoc loc, Type type,
346+
llvm::function_ref<void(Type)> diagnose) {
347+
if (!ctx.LangOpts.hasFeature(Feature::WarnUnsafe))
348+
return;
349+
350+
if (!type->isUnsafe() && !type->getCanonicalType()->isUnsafe())
351+
return;
352+
353+
// Look for a specific @unsafe nominal type.
354+
Type specificType;
355+
type.findIf([&specificType](Type type) {
356+
if (auto typeDecl = type->getAnyNominal()) {
357+
if (typeDecl->isUnsafe()) {
358+
specificType = type;
359+
return false;
360+
}
361+
}
362+
363+
return false;
364+
});
365+
366+
diagnose(specificType ? specificType : type);
367+
}

lib/Sema/TypeCheckUnsafe.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ bool isUnsafe(ConcreteDeclRef declRef);
6464
bool isUnsafeInConformance(const ValueDecl *requirement,
6565
const Witness &witness,
6666
NormalProtocolConformance *conformance);
67+
68+
/// If the given type involves an unsafe type, diagnose it by calling the
69+
/// diagnose function with the most specific unsafe type that can be provided.
70+
void diagnoseUnsafeType(ASTContext &ctx, SourceLoc loc, Type type,
71+
llvm::function_ref<void(Type)> diagnose);
72+
6773
}
6874

6975
#endif // SWIFT_SEMA_TYPE_CHECK_UNSAFE_H

test/Unsafe/safe.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,26 @@ class MyRange {
104104
}
105105
}
106106

107+
func casting(value: Any, i: Int) {
108+
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
109+
_ = value as? UnsafeType // expected-note{{reference to unsafe type 'UnsafeType'}}
110+
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
111+
_ = value as! UnsafeType // expected-note{{reference to unsafe type 'UnsafeType'}}
112+
113+
_ = unsafe value as? UnsafeType
114+
_ = unsafe value as! UnsafeType
115+
116+
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
117+
_ = i as any P // expected-note{{@unsafe conformance of 'Int' to protocol 'P' involves unsafe code}}
118+
}
119+
120+
func metatypes() {
121+
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
122+
let _: Any.Type = UnsafeType.self // expected-note{{reference to unsafe type 'UnsafeType'}}
123+
124+
let _: Any.Type = unsafe UnsafeType.self
125+
}
126+
107127
// Parsing of `unsafe` expressions.
108128
func testUnsafePositionError() -> Int {
109129
return 3 + unsafe unsafeInt() // expected-error{{'unsafe' cannot appear to the right of a non-assignment operator}}

0 commit comments

Comments
 (0)