Skip to content

Commit c2e36de

Browse files
authored
Merge pull request #79424 from DougGregor/se-0458-match-proposal
[SE-0458] Bring implementation in line with the latest proposal revision
2 parents 9020533 + c0fb9f9 commit c2e36de

22 files changed

+397
-119
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8149,15 +8149,22 @@ NOTE(note_reference_exclusivity_unchecked,none,
81498149
NOTE(note_use_of_unsafe_conformance_is_unsafe,none,
81508150
"@unsafe conformance of %0 to %kind1 involves unsafe code",
81518151
(Type, const ValueDecl *))
8152+
NOTE(note_unsafe_storage,none,
8153+
"%kindbase1 involves unsafe type %2", (bool, const ValueDecl *, Type))
81528154

8153-
GROUPED_WARNING(decl_signature_involves_unsafe,Unsafe,none,
8154-
"%kindbase0 has an interface that involves unsafe types",
8155+
GROUPED_WARNING(decl_unsafe_storage,Unsafe,none,
8156+
"%kindbase0 has storage involving unsafe types",
81558157
(const Decl *))
8156-
NOTE(decl_signature_mark_unsafe,none,
8157-
"add '@unsafe' to indicate that this declaration is unsafe to use",
8158+
NOTE(decl_storage_mark_unsafe,none,
8159+
"add '@unsafe' if this type is also unsafe to use",
81588160
())
8159-
NOTE(decl_signature_mark_safe,none,
8160-
"add '@safe' to indicate that this declaration is memory-safe to use", ())
8161+
NOTE(decl_storage_mark_safe,none,
8162+
"add '@safe' if this type encapsulates the unsafe storage in "
8163+
"a safe interface", ())
8164+
8165+
GROUPED_WARNING(unsafe_superclass,Unsafe,none,
8166+
"%kindbase0 has superclass involving unsafe type %1",
8167+
(const ValueDecl *, Type))
81618168

81628169
GROUPED_WARNING(conformance_involves_unsafe,Unsafe,none,
81638170
"conformance of %0 to %kind1 involves unsafe code; use '@unsafe' to "

include/swift/AST/UnsafeUse.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ class UnsafeUse {
4545
NonisolatedUnsafe,
4646
/// A reference to an unsafe declaration.
4747
ReferenceToUnsafe,
48+
/// A reference to an unsafe storage.
49+
ReferenceToUnsafeStorage,
4850
/// A reference to a typealias that is not itself unsafe, but has
4951
/// an unsafe underlying type.
5052
ReferenceToUnsafeThroughTypealias,
@@ -113,6 +115,7 @@ class UnsafeUse {
113115
kind == ExclusivityUnchecked ||
114116
kind == NonisolatedUnsafe ||
115117
kind == ReferenceToUnsafe ||
118+
kind == ReferenceToUnsafeStorage ||
116119
kind == ReferenceToUnsafeThroughTypealias ||
117120
kind == CallToUnsafe);
118121

@@ -179,6 +182,12 @@ class UnsafeUse {
179182
decl, type, location);
180183
}
181184

185+
static UnsafeUse forReferenceToUnsafeStorage(const Decl *decl,
186+
Type type,
187+
SourceLoc location) {
188+
return forReference(ReferenceToUnsafeStorage, decl, type, location);
189+
}
190+
182191
static UnsafeUse forReferenceToUnsafeThroughTypealias(const Decl *decl,
183192
Type type,
184193
SourceLoc location) {
@@ -215,6 +224,7 @@ class UnsafeUse {
215224
case ExclusivityUnchecked:
216225
case NonisolatedUnsafe:
217226
case ReferenceToUnsafe:
227+
case ReferenceToUnsafeStorage:
218228
case ReferenceToUnsafeThroughTypealias:
219229
case CallToUnsafe:
220230
return SourceLoc(
@@ -246,6 +256,7 @@ class UnsafeUse {
246256
case ExclusivityUnchecked:
247257
case NonisolatedUnsafe:
248258
case ReferenceToUnsafe:
259+
case ReferenceToUnsafeStorage:
249260
case ReferenceToUnsafeThroughTypealias:
250261
case CallToUnsafe:
251262
storage.entity.location = loc.getOpaquePointerValue();
@@ -266,6 +277,7 @@ class UnsafeUse {
266277
case ExclusivityUnchecked:
267278
case NonisolatedUnsafe:
268279
case ReferenceToUnsafe:
280+
case ReferenceToUnsafeStorage:
269281
case ReferenceToUnsafeThroughTypealias:
270282
case CallToUnsafe:
271283
return storage.entity.decl;
@@ -299,6 +311,7 @@ class UnsafeUse {
299311
case NonisolatedUnsafe:
300312
case ReferenceToUnsafe:
301313
case ReferenceToUnsafeThroughTypealias:
314+
case ReferenceToUnsafeStorage:
302315
case CallToUnsafe:
303316
case UnsafeConformance:
304317
case PreconcurrencyImport:
@@ -324,6 +337,7 @@ class UnsafeUse {
324337
case ExclusivityUnchecked:
325338
case NonisolatedUnsafe:
326339
case ReferenceToUnsafe:
340+
case ReferenceToUnsafeStorage:
327341
case ReferenceToUnsafeThroughTypealias:
328342
case CallToUnsafe:
329343
return storage.entity.type;
@@ -348,6 +362,7 @@ class UnsafeUse {
348362
case ExclusivityUnchecked:
349363
case NonisolatedUnsafe:
350364
case ReferenceToUnsafe:
365+
case ReferenceToUnsafeStorage:
351366
case ReferenceToUnsafeThroughTypealias:
352367
case CallToUnsafe:
353368
case PreconcurrencyImport:

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace swift {
3131
class Decl;
3232
class DeclName;
3333
class EnumDecl;
34+
enum class ExplicitSafety;
3435

3536
/// The input type for a clang direct lookup request.
3637
struct ClangDirectLookupDescriptor final {
@@ -564,6 +565,25 @@ class ClangTypeEscapability
564565
void simple_display(llvm::raw_ostream &out, EscapabilityLookupDescriptor desc);
565566
SourceLoc extractNearestSourceLoc(EscapabilityLookupDescriptor desc);
566567

568+
/// Determine the safety of the given Clang declaration.
569+
class ClangDeclExplicitSafety
570+
: public SimpleRequest<ClangDeclExplicitSafety,
571+
ExplicitSafety(SafeUseOfCxxDeclDescriptor),
572+
RequestFlags::Cached> {
573+
public:
574+
using SimpleRequest::SimpleRequest;
575+
576+
// Source location
577+
SourceLoc getNearestLoc() const { return SourceLoc(); };
578+
bool isCached() const;
579+
580+
private:
581+
friend SimpleRequest;
582+
583+
// Evaluation.
584+
ExplicitSafety evaluate(Evaluator &evaluator, SafeUseOfCxxDeclDescriptor desc) const;
585+
};
586+
567587
#define SWIFT_TYPEID_ZONE ClangImporter
568588
#define SWIFT_TYPEID_HEADER "swift/ClangImporter/ClangImporterTypeIDZone.def"
569589
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/ClangImporter/ClangImporterTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,6 @@ SWIFT_REQUEST(ClangImporter, CustomRefCountingOperation,
4545
SWIFT_REQUEST(ClangImporter, ClangTypeEscapability,
4646
CxxEscapability(EscapabilityLookupDescriptor), Cached,
4747
NoLocationInfo)
48+
SWIFT_REQUEST(ClangImporter, ClangDeclExplicitSafety,
49+
ExplicitSafety(SafeUseOfCxxDeclDescriptor), Cached,
50+
NoLocationInfo)

lib/AST/Decl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,14 @@ ExplicitSafety Decl::getExplicitSafety() const {
11501150
if (auto safety = getExplicitSafetyFromAttrs(this))
11511151
return *safety;
11521152

1153+
// If this declaration is from C, ask the Clang importer.
1154+
if (auto clangDecl = getClangDecl()) {
1155+
ASTContext &ctx = getASTContext();
1156+
return evaluateOrDefault(
1157+
ctx.evaluator, ClangDeclExplicitSafety({clangDecl, ctx}),
1158+
ExplicitSafety::Unspecified);
1159+
}
1160+
11531161
// Inference: Check the enclosing context.
11541162
if (auto enclosingDC = getDeclContext()) {
11551163
// Is this an extension with @safe or @unsafe on it?

lib/ClangImporter/ClangImporter.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8215,6 +8215,106 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate(
82158215
return {CustomRefCountingOperationResult::tooManyFound, nullptr, name};
82168216
}
82178217

8218+
/// Check whether the given Clang type involves an unsafe type.
8219+
static bool hasUnsafeType(
8220+
Evaluator &evaluator, ASTContext &swiftContext, clang::QualType clangType
8221+
) {
8222+
// Handle pointers.
8223+
auto pointeeType = clangType->getPointeeType();
8224+
if (!pointeeType.isNull()) {
8225+
// Function pointers are okay.
8226+
if (pointeeType->isFunctionType())
8227+
return false;
8228+
8229+
// Pointers to record types are okay if they come in as foreign reference
8230+
// types.
8231+
if (auto recordDecl = pointeeType->getAsRecordDecl()) {
8232+
if (hasImportAsRefAttr(recordDecl))
8233+
return false;
8234+
}
8235+
8236+
// All other pointers are considered unsafe.
8237+
return true;
8238+
}
8239+
8240+
// Handle records recursively.
8241+
if (auto recordDecl = clangType->getAsTagDecl()) {
8242+
auto safety = evaluateOrDefault(
8243+
evaluator,
8244+
ClangDeclExplicitSafety({recordDecl, swiftContext}),
8245+
ExplicitSafety::Unspecified);
8246+
switch (safety) {
8247+
case ExplicitSafety::Unsafe:
8248+
return true;
8249+
8250+
case ExplicitSafety::Safe:
8251+
case ExplicitSafety::Unspecified:
8252+
return false;
8253+
}
8254+
}
8255+
8256+
// Everything else is safe.
8257+
return false;
8258+
}
8259+
8260+
ExplicitSafety ClangDeclExplicitSafety::evaluate(
8261+
Evaluator &evaluator,
8262+
SafeUseOfCxxDeclDescriptor desc
8263+
) const {
8264+
// FIXME: Somewhat duplicative with importAsUnsafe.
8265+
// FIXME: Also similar to hasPointerInSubobjects
8266+
// FIXME: should probably also subsume IsSafeUseOfCxxDecl
8267+
8268+
// Explicitly unsafe.
8269+
auto decl = desc.decl;
8270+
if (hasUnsafeAPIAttr(decl) || hasSwiftAttribute(decl, "unsafe"))
8271+
return ExplicitSafety::Unsafe;
8272+
8273+
// Explicitly safe.
8274+
if (hasSwiftAttribute(decl, "safe"))
8275+
return ExplicitSafety::Safe;
8276+
8277+
// Enums are always safe.
8278+
if (isa<clang::EnumDecl>(decl))
8279+
return ExplicitSafety::Safe;
8280+
8281+
// If it's not a record, leave it unspecified.
8282+
auto recordDecl = dyn_cast<clang::RecordDecl>(decl);
8283+
if (!recordDecl)
8284+
return ExplicitSafety::Unspecified;
8285+
8286+
// Escapable and non-escapable annotations imply that the declaration is
8287+
// safe.
8288+
if (hasNonEscapableAttr(recordDecl) || hasEscapableAttr(recordDecl))
8289+
return ExplicitSafety::Safe;
8290+
8291+
// If we don't have a definition, leave it unspecified.
8292+
recordDecl = recordDecl->getDefinition();
8293+
if (!recordDecl)
8294+
return ExplicitSafety::Unspecified;
8295+
8296+
// If this is a C++ class, check its bases.
8297+
if (auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(recordDecl)) {
8298+
for (auto base : cxxRecordDecl->bases()) {
8299+
if (hasUnsafeType(evaluator, desc.ctx, base.getType()))
8300+
return ExplicitSafety::Unsafe;
8301+
}
8302+
}
8303+
8304+
// Check the fields.
8305+
for (auto field : recordDecl->fields()) {
8306+
if (hasUnsafeType(evaluator, desc.ctx, field->getType()))
8307+
return ExplicitSafety::Unsafe;
8308+
}
8309+
8310+
// Okay, call it safe.
8311+
return ExplicitSafety::Safe;
8312+
}
8313+
8314+
bool ClangDeclExplicitSafety::isCached() const {
8315+
return isa<clang::RecordDecl>(std::get<0>(getStorage()).decl);
8316+
}
8317+
82188318
void ClangImporter::withSymbolicFeatureEnabled(
82198319
llvm::function_ref<void(void)> callback) {
82208320
llvm::SaveAndRestore<bool> oldImportSymbolicCXXDecls(

lib/Sema/TypeCheckAccess.cpp

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,7 +1332,7 @@ class AccessControlChecker : public AccessControlCheckerBase,
13321332
if (macroDecl) {
13331333
diagnoseDeclAvailability(
13341334
macroDecl, customAttr->getTypeRepr()->getSourceRange(), nullptr,
1335-
ExportContext::forDeclSignature(const_cast<Decl *>(D), nullptr),
1335+
ExportContext::forDeclSignature(const_cast<Decl *>(D)),
13361336
std::nullopt);
13371337
}
13381338
}
@@ -2678,35 +2678,9 @@ void swift::checkAccessControl(Decl *D) {
26782678
if (isa<AccessorDecl>(D))
26792679
return;
26802680

2681-
// If we need to gather all unsafe uses from the declaration signature,
2682-
// do so now.
2683-
llvm::SmallVector<UnsafeUse, 2> unsafeUsesVec;
2684-
llvm::SmallVectorImpl<UnsafeUse> *unsafeUses = nullptr;
2685-
if (D->getASTContext().LangOpts.hasFeature(Feature::WarnUnsafe) &&
2686-
!D->isImplicit() &&
2687-
D->getExplicitSafety() == ExplicitSafety::Unspecified) {
2688-
unsafeUses = &unsafeUsesVec;
2689-
}
2690-
2691-
auto where = ExportContext::forDeclSignature(D, unsafeUses);
2681+
auto where = ExportContext::forDeclSignature(D);
26922682
if (where.isImplicit())
26932683
return;
26942684

26952685
DeclAvailabilityChecker(where).visit(D);
2696-
2697-
// If there were any unsafe uses, this declaration needs "@unsafe".
2698-
if (!unsafeUsesVec.empty()) {
2699-
D->diagnose(diag::decl_signature_involves_unsafe, D);
2700-
2701-
ASTContext &ctx = D->getASTContext();
2702-
auto insertLoc = D->getAttributeInsertionLoc(/*forModifier=*/false);
2703-
2704-
ctx.Diags.diagnose(insertLoc, diag::decl_signature_mark_unsafe)
2705-
.fixItInsert(insertLoc, "@unsafe ");
2706-
ctx.Diags.diagnose(insertLoc, diag::decl_signature_mark_safe)
2707-
.fixItInsert(insertLoc, "@safe ");
2708-
2709-
std::for_each(unsafeUsesVec.begin(), unsafeUsesVec.end(),
2710-
diagnoseUnsafeUse);
2711-
}
27122686
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3280,7 +3280,7 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
32803280
return nullptr;
32813281
}
32823282

3283-
auto where = ExportContext::forDeclSignature(D, nullptr);
3283+
auto where = ExportContext::forDeclSignature(D);
32843284
diagnoseDeclAvailability(mainFunction, attr->getRange(), nullptr, where,
32853285
std::nullopt);
32863286

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,7 @@ static void computeExportContextBits(ASTContext &Ctx, Decl *D, bool *spi,
247247
}
248248
}
249249

250-
ExportContext ExportContext::forDeclSignature(
251-
Decl *D, llvm::SmallVectorImpl<UnsafeUse> *unsafeUses) {
250+
ExportContext ExportContext::forDeclSignature(Decl *D) {
252251
auto &Ctx = D->getASTContext();
253252

254253
auto *DC = D->getInnermostDeclContext();
@@ -264,7 +263,7 @@ ExportContext ExportContext::forDeclSignature(
264263

265264
bool exported = ::isExported(D);
266265

267-
return ExportContext(DC, availabilityContext, fragileKind, unsafeUses,
266+
return ExportContext(DC, availabilityContext, fragileKind, nullptr,
268267
spi, exported, implicit);
269268
}
270269

@@ -287,8 +286,7 @@ ExportContext ExportContext::forFunctionBody(DeclContext *DC, SourceLoc loc) {
287286
ExportContext ExportContext::forConformance(DeclContext *DC,
288287
ProtocolDecl *proto) {
289288
assert(isa<ExtensionDecl>(DC) || isa<NominalTypeDecl>(DC));
290-
auto where = forDeclSignature(DC->getInnermostDeclarationDeclContext(),
291-
nullptr);
289+
auto where = forDeclSignature(DC->getInnermostDeclarationDeclContext());
292290

293291
where.Exported &= proto->getFormalAccessScope(
294292
DC, /*usableFromInlineAsPublic*/true).isPublic();
@@ -2914,7 +2912,7 @@ void swift::diagnoseOverrideOfUnavailableDecl(ValueDecl *override,
29142912

29152913
// FIXME: [availability] Take an unsatisfied constraint as input instead of
29162914
// recomputing it.
2917-
ExportContext where = ExportContext::forDeclSignature(override, nullptr);
2915+
ExportContext where = ExportContext::forDeclSignature(override);
29182916
auto constraint =
29192917
getAvailabilityConstraintsForDecl(base, where.getAvailability())
29202918
.getPrimaryConstraint();

lib/Sema/TypeCheckAvailability.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,7 @@ class ExportContext {
127127
///
128128
/// If the declaration is exported, the resulting context is restricted to
129129
/// referencing exported types only. Otherwise it can reference anything.
130-
static ExportContext forDeclSignature(
131-
Decl *D, llvm::SmallVectorImpl<UnsafeUse> *unsafeUses);
130+
static ExportContext forDeclSignature(Decl *D);
132131

133132
/// Create an instance describing the declarations that can be referenced
134133
/// from the given function's body.

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2387,12 +2387,19 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
23872387
"`" + VD->getBaseName().userFacingName().str() + "`");
23882388
}
23892389

2390-
// Expand extension macros.
23912390
if (auto *nominal = dyn_cast<NominalTypeDecl>(VD)) {
2391+
// Expand extension macros.
23922392
(void)evaluateOrDefault(
23932393
Ctx.evaluator,
23942394
ExpandExtensionMacros{nominal},
23952395
{ });
2396+
2397+
// If strict memory safety checking is enabled, check the storage
2398+
// of the nominal type.
2399+
if (Ctx.LangOpts.hasFeature(Feature::WarnUnsafe) &&
2400+
!isa<ProtocolDecl>(nominal)) {
2401+
checkUnsafeStorage(nominal);
2402+
}
23962403
}
23972404
}
23982405
}

0 commit comments

Comments
 (0)