Skip to content

Commit 4395537

Browse files
committed
Introduce the @safe attribute as described in the opt-in safety checking proposal
1 parent 9796d1b commit 4395537

24 files changed

+182
-115
lines changed

include/swift/AST/Decl.h

+35-6
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,16 @@ enum class AssociatedValueCheck {
235235
HasAssociatedValues,
236236
};
237237

238+
/// An explicit declaration of the safety of
239+
enum class ExplicitSafety {
240+
/// There was no explicit declaration of the safety of the given entity.
241+
Unspecified,
242+
/// The entity was explicitly declared safe with @safe.
243+
Safe,
244+
/// The entity was explicitly declared unsafe with @unsafe.
245+
Unsafe
246+
};
247+
238248
/// Diagnostic printing of \c StaticSpellingKind.
239249
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, StaticSpellingKind SSK);
240250

@@ -859,7 +869,6 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
859869
friend class ExpandPeerMacroRequest;
860870
friend class GlobalActorAttributeRequest;
861871
friend class SPIGroupsRequest;
862-
friend class IsUnsafeRequest;
863872

864873
private:
865874
llvm::PointerUnion<DeclContext *, ASTContext *> Context;
@@ -1203,9 +1212,9 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
12031212
/// Whether this declaration predates the introduction of concurrency.
12041213
bool preconcurrency() const;
12051214

1206-
/// Whether this declaration is considered "unsafe", i.e., should not be
1207-
/// used in a "safe" dialect.
1208-
bool isUnsafe() const;
1215+
/// Query whether this declaration was explicitly declared to be safe or
1216+
/// unsafe.
1217+
ExplicitSafety getExplicitSafety() const;
12091218

12101219
private:
12111220
bool isUnsafeComputed() const {
@@ -1816,8 +1825,13 @@ struct InheritedEntry : public TypeLoc {
18161825
bool isPreconcurrency() const {
18171826
return getOptions().contains(ProtocolConformanceFlags::Preconcurrency);
18181827
}
1819-
bool isUnsafe() const {
1820-
return getOptions().contains(ProtocolConformanceFlags::Unsafe);
1828+
1829+
ExplicitSafety getExplicitSafety() const {
1830+
if (getOptions().contains(ProtocolConformanceFlags::Unsafe))
1831+
return ExplicitSafety::Unsafe;
1832+
if (getOptions().contains(ProtocolConformanceFlags::Safe))
1833+
return ExplicitSafety::Safe;
1834+
return ExplicitSafety::Unspecified;
18211835
}
18221836

18231837
bool isSuppressed() const { return IsSuppressed; }
@@ -1826,6 +1840,21 @@ struct InheritedEntry : public TypeLoc {
18261840
RawOptions = (getOptions() | flag).toRaw();
18271841
}
18281842

1843+
void setOption(ExplicitSafety safety) {
1844+
RawOptions = (getOptions() - ProtocolConformanceFlags::Unsafe
1845+
- ProtocolConformanceFlags::Safe).toRaw();
1846+
switch (safety) {
1847+
case ExplicitSafety::Unspecified:
1848+
break;
1849+
case ExplicitSafety::Safe:
1850+
RawOptions = (getOptions() | ProtocolConformanceFlags::Safe).toRaw();
1851+
break;
1852+
case ExplicitSafety::Unsafe:
1853+
RawOptions = (getOptions() | ProtocolConformanceFlags::Unsafe).toRaw();
1854+
break;
1855+
}
1856+
}
1857+
18291858
void setSuppressed() {
18301859
assert(!IsSuppressed && "setting suppressed again!?");
18311860
IsSuppressed = true;

include/swift/AST/DeclAttr.def

+6-2
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ SIMPLE_DECL_ATTR(sensitive, Sensitive,
516516
SIMPLE_DECL_ATTR(unsafe, Unsafe,
517517
OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType |
518518
OnExtension | OnTypeAlias | OnEnumElement | UserInaccessible |
519-
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
519+
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
520520
160)
521521

522522
DECL_ATTR(lifetime, Lifetime,
@@ -531,7 +531,11 @@ SIMPLE_DECL_ATTR(_addressableForDependencies, AddressableForDependencies,
531531
OnNominalType | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UserInaccessible,
532532
163)
533533

534-
// 164 was the never-shipped @safe attribute and can be reused
534+
SIMPLE_DECL_ATTR(safe, Safe,
535+
OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType |
536+
OnExtension | OnEnumElement | UserInaccessible |
537+
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
538+
164)
535539

536540
DECL_ATTR(abi, ABI,
537541
OnAbstractFunction | OnVar /* will eventually add types */ | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,

include/swift/AST/ProtocolConformance.h

+8-3
Original file line numberDiff line numberDiff line change
@@ -654,9 +654,14 @@ class NormalProtocolConformance : public RootProtocolConformance,
654654
/// known.
655655
SourceLoc getPreconcurrencyLoc() const { return PreconcurrencyLoc; }
656656

657-
/// Whether this is an "unsafe" conformance.
658-
bool isUnsafe() const {
659-
return getOptions().contains(ProtocolConformanceFlags::Unsafe);
657+
/// Query whether this conformance was explicitly declared to be safe or
658+
/// unsafe.
659+
ExplicitSafety getExplicitSafety() const {
660+
if (getOptions().contains(ProtocolConformanceFlags::Unsafe))
661+
return ExplicitSafety::Unsafe;
662+
if (getOptions().contains(ProtocolConformanceFlags::Safe))
663+
return ExplicitSafety::Safe;
664+
return ExplicitSafety::Unspecified;
660665
}
661666

662667
/// Determine whether we've lazily computed the associated conformance array

include/swift/AST/ProtocolConformanceOptions.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ enum class ProtocolConformanceFlags {
3434
/// @retroactive conformance
3535
Retroactive = 0x08,
3636

37+
/// @safe conformance
38+
Safe = 0x10,
39+
3740
// Note: whenever you add a bit here, update
3841
// NumProtocolConformanceOptions below.
3942
};
@@ -49,7 +52,7 @@ inline ProtocolConformanceOptions operator|(
4952
}
5053

5154
enum : unsigned {
52-
NumProtocolConformanceOptions = 4
55+
NumProtocolConformanceOptions = 5
5356
};
5457

5558
} // end namespace swift

include/swift/AST/TypeCheckRequests.h

-18
Original file line numberDiff line numberDiff line change
@@ -5175,24 +5175,6 @@ void simple_display(llvm::raw_ostream &out,
51755175
RegexLiteralPatternFeatureKind kind);
51765176
SourceLoc extractNearestSourceLoc(RegexLiteralPatternFeatureKind kind);
51775177

5178-
class IsUnsafeRequest
5179-
: public SimpleRequest<IsUnsafeRequest,
5180-
bool(Decl *decl),
5181-
RequestFlags::SeparatelyCached> {
5182-
public:
5183-
using SimpleRequest::SimpleRequest;
5184-
5185-
private:
5186-
friend SimpleRequest;
5187-
5188-
bool evaluate(Evaluator &evaluator, Decl *decl) const;
5189-
5190-
public:
5191-
bool isCached() const { return true; }
5192-
std::optional<bool> getCachedResult() const;
5193-
void cacheResult(bool value) const;
5194-
};
5195-
51965178
class GenericTypeParamDeclGetValueTypeRequest
51975179
: public SimpleRequest<GenericTypeParamDeclGetValueTypeRequest,
51985180
Type(GenericTypeParamDecl *decl),

include/swift/AST/TypeCheckerTypeIDZone.def

-3
Original file line numberDiff line numberDiff line change
@@ -605,9 +605,6 @@ SWIFT_REQUEST(TypeChecker, CaptureInfoRequest,
605605
SWIFT_REQUEST(TypeChecker, ParamCaptureInfoRequest,
606606
CaptureInfo(ParamDecl *),
607607
SeparatelyCached, NoLocationInfo)
608-
SWIFT_REQUEST(TypeChecker, IsUnsafeRequest,
609-
bool(Decl *),
610-
SeparatelyCached, NoLocationInfo)
611608
SWIFT_REQUEST(TypeChecker, CustomDerivativesRequest,
612609
CustomDerivativesResult(SourceFile *),
613610
Cached, NoLocationInfo)

lib/AST/ASTContext.cpp

+12-6
Original file line numberDiff line numberDiff line change
@@ -4076,7 +4076,8 @@ get(GenericTypeDecl *TheDecl, Type Parent, const ASTContext &C) {
40764076
UnboundGenericType::Profile(ID, TheDecl, Parent);
40774077
void *InsertPos = nullptr;
40784078
RecursiveTypeProperties properties;
4079-
if (TheDecl->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
4079+
if (TheDecl->getExplicitSafety() == ExplicitSafety::Unsafe)
4080+
properties |= RecursiveTypeProperties::IsUnsafe;
40804081
if (Parent) properties |= Parent->getRecursiveProperties();
40814082

40824083
auto arena = getArena(properties);
@@ -4129,7 +4130,8 @@ BoundGenericType *BoundGenericType::get(NominalTypeDecl *TheDecl,
41294130
llvm::FoldingSetNodeID ID;
41304131
BoundGenericType::Profile(ID, TheDecl, Parent, GenericArgs);
41314132
RecursiveTypeProperties properties;
4132-
if (TheDecl->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
4133+
if (TheDecl->getExplicitSafety() == ExplicitSafety::Unsafe)
4134+
properties |= RecursiveTypeProperties::IsUnsafe;
41334135
if (Parent) properties |= Parent->getRecursiveProperties();
41344136
for (Type Arg : GenericArgs) {
41354137
properties |= Arg->getRecursiveProperties();
@@ -4211,7 +4213,8 @@ EnumType::EnumType(EnumDecl *TheDecl, Type Parent, const ASTContext &C,
42114213

42124214
EnumType *EnumType::get(EnumDecl *D, Type Parent, const ASTContext &C) {
42134215
RecursiveTypeProperties properties;
4214-
if (D->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
4216+
if (D->getExplicitSafety() == ExplicitSafety::Unsafe)
4217+
properties |= RecursiveTypeProperties::IsUnsafe;
42154218
if (Parent) properties |= Parent->getRecursiveProperties();
42164219
auto arena = getArena(properties);
42174220

@@ -4228,7 +4231,8 @@ StructType::StructType(StructDecl *TheDecl, Type Parent, const ASTContext &C,
42284231

42294232
StructType *StructType::get(StructDecl *D, Type Parent, const ASTContext &C) {
42304233
RecursiveTypeProperties properties;
4231-
if (D->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
4234+
if (D->getExplicitSafety() == ExplicitSafety::Unsafe)
4235+
properties |= RecursiveTypeProperties::IsUnsafe;
42324236
if (Parent) properties |= Parent->getRecursiveProperties();
42334237
auto arena = getArena(properties);
42344238

@@ -4245,7 +4249,8 @@ ClassType::ClassType(ClassDecl *TheDecl, Type Parent, const ASTContext &C,
42454249

42464250
ClassType *ClassType::get(ClassDecl *D, Type Parent, const ASTContext &C) {
42474251
RecursiveTypeProperties properties;
4248-
if (D->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
4252+
if (D->getExplicitSafety() == ExplicitSafety::Unsafe)
4253+
properties |= RecursiveTypeProperties::IsUnsafe;
42494254
if (Parent) properties |= Parent->getRecursiveProperties();
42504255
auto arena = getArena(properties);
42514256

@@ -5396,7 +5401,8 @@ OptionalType *OptionalType::get(Type base) {
53965401
ProtocolType *ProtocolType::get(ProtocolDecl *D, Type Parent,
53975402
const ASTContext &C) {
53985403
RecursiveTypeProperties properties;
5399-
if (D->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
5404+
if (D->getExplicitSafety() == ExplicitSafety::Unsafe)
5405+
properties |= RecursiveTypeProperties::IsUnsafe;
54005406
if (Parent) properties |= Parent->getRecursiveProperties();
54015407
auto arena = getArena(properties);
54025408

lib/AST/ASTDumper.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -3857,6 +3857,7 @@ class PrintAttribute : public AttributeVisitor<PrintAttribute, void, StringRef>,
38573857
requires_stored_property_inits)
38583858
TRIVIAL_ATTR_PRINTER(ResultBuilder, result_builder)
38593859
TRIVIAL_ATTR_PRINTER(Rethrows, rethrows)
3860+
TRIVIAL_ATTR_PRINTER(Safe, safe)
38603861
TRIVIAL_ATTR_PRINTER(SPIOnly, spi_only)
38613862
TRIVIAL_ATTR_PRINTER(Sendable, sendable)
38623863
TRIVIAL_ATTR_PRINTER(Sensitive, sensitive)

lib/AST/ASTPrinter.cpp

+11-1
Original file line numberDiff line numberDiff line change
@@ -2895,8 +2895,18 @@ void PrintAST::printInherited(const Decl *decl) {
28952895
Printer << "@retroactive ";
28962896
if (inherited.isPreconcurrency())
28972897
Printer << "@preconcurrency ";
2898-
if (inherited.isUnsafe())
2898+
switch (inherited.getExplicitSafety()) {
2899+
case ExplicitSafety::Unspecified:
2900+
break;
2901+
2902+
case ExplicitSafety::Safe:
2903+
Printer << "@safe ";
2904+
break;
2905+
2906+
case ExplicitSafety::Unsafe:
28992907
Printer << "@unsafe ";
2908+
break;
2909+
}
29002910
if (inherited.isSuppressed())
29012911
Printer << "~";
29022912
});

lib/AST/Decl.cpp

+48-5
Original file line numberDiff line numberDiff line change
@@ -1087,11 +1087,54 @@ bool Decl::preconcurrency() const {
10871087
return false;
10881088
}
10891089

1090-
bool Decl::isUnsafe() const {
1091-
return evaluateOrDefault(
1092-
getASTContext().evaluator,
1093-
IsUnsafeRequest{const_cast<Decl *>(this)},
1094-
false);
1090+
/// Look at the attributes to determine whether they involve an attribute
1091+
/// that explicitly specifies the safety of the declaration.
1092+
static std::optional<ExplicitSafety>
1093+
getExplicitSafetyFromAttrs(const Decl *decl) {
1094+
// If it's marked @unsafe, it's unsafe.
1095+
if (decl->getAttrs().hasAttribute<UnsafeAttr>())
1096+
return ExplicitSafety::Unsafe;
1097+
1098+
// If it's marked @safe, it's safe.
1099+
if (decl->getAttrs().hasAttribute<SafeAttr>())
1100+
return ExplicitSafety::Safe;
1101+
1102+
return std::nullopt;
1103+
}
1104+
1105+
ExplicitSafety Decl::getExplicitSafety() const {
1106+
// Check the attributes on the declaration itself.
1107+
if (auto safety = getExplicitSafetyFromAttrs(this))
1108+
return *safety;
1109+
1110+
// Inference: Check the enclosing context.
1111+
if (auto enclosingDC = getDeclContext()) {
1112+
// Is this an extension with @safe or @unsafe on it?
1113+
if (auto ext = dyn_cast<ExtensionDecl>(enclosingDC)) {
1114+
if (auto extSafety = getExplicitSafetyFromAttrs(ext))
1115+
return *extSafety;
1116+
}
1117+
1118+
if (auto enclosingNominal = enclosingDC->getSelfNominalTypeDecl())
1119+
if (auto nominalSafety = getExplicitSafetyFromAttrs(enclosingNominal))
1120+
return *nominalSafety;
1121+
}
1122+
1123+
// If an extension extends an unsafe nominal type, it's unsafe.
1124+
if (auto ext = dyn_cast<ExtensionDecl>(this)) {
1125+
if (auto nominal = ext->getExtendedNominal())
1126+
if (nominal->getExplicitSafety() == ExplicitSafety::Unsafe)
1127+
return ExplicitSafety::Unsafe;
1128+
}
1129+
1130+
// If this is a pattern binding declaration, check whether the first
1131+
// variable is @unsafe.
1132+
if (auto patternBinding = dyn_cast<PatternBindingDecl>(this)) {
1133+
if (auto var = patternBinding->getSingleVar())
1134+
return var->getExplicitSafety();
1135+
}
1136+
1137+
return ExplicitSafety::Unspecified;
10951138
}
10961139

10971140
Type AbstractFunctionDecl::getThrownInterfaceType() const {

lib/AST/Effects.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ bool AbstractFunctionDecl::hasEffect(EffectKind kind) const {
8686
case EffectKind::Async:
8787
return hasAsync();
8888
case EffectKind::Unsafe:
89-
return isUnsafe();
89+
return getExplicitSafety() == ExplicitSafety::Unsafe;
9090
}
9191
llvm_unreachable("Bad effect kind");
9292
}

lib/AST/Type.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3566,7 +3566,7 @@ RecursiveTypeProperties ArchetypeType::archetypeProperties(
35663566
properties |= subs.getRecursiveProperties();
35673567

35683568
for (auto proto : conformsTo) {
3569-
if (proto->isUnsafe()) {
3569+
if (proto->getExplicitSafety() == ExplicitSafety::Unsafe) {
35703570
properties |= RecursiveTypeProperties::IsUnsafe;
35713571
break;
35723572
}

lib/AST/TypeCheckRequests.cpp

-17
Original file line numberDiff line numberDiff line change
@@ -2714,23 +2714,6 @@ void ParamCaptureInfoRequest::cacheResult(CaptureInfo info) const {
27142714
param->setDefaultArgumentCaptureInfo(info);
27152715
}
27162716

2717-
//----------------------------------------------------------------------------//
2718-
// IsUnsafeRequest computation.
2719-
//----------------------------------------------------------------------------//
2720-
2721-
std::optional<bool> IsUnsafeRequest::getCachedResult() const {
2722-
auto *decl = std::get<0>(getStorage());
2723-
if (!decl->isUnsafeComputed())
2724-
return std::nullopt;
2725-
2726-
return decl->isUnsafeRaw();
2727-
}
2728-
2729-
void IsUnsafeRequest::cacheResult(bool value) const {
2730-
auto *decl = std::get<0>(getStorage());
2731-
decl->setUnsafe(value);
2732-
}
2733-
27342717
//----------------------------------------------------------------------------//
27352718
// SemanticAvailableAttrRequest computation.
27362719
//----------------------------------------------------------------------------//

lib/ASTGen/Sources/ASTGen/DeclAttrs.swift

+1
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ extension ASTGenVisitor {
252252
.propertyWrapper,
253253
.requiresStoredPropertyInits,
254254
.resultBuilder,
255+
.safe,
255256
.sendable,
256257
.sensitive,
257258
.spiOnly,

lib/Sema/TypeCheckAccess.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -2683,7 +2683,8 @@ void swift::checkAccessControl(Decl *D) {
26832683
llvm::SmallVector<UnsafeUse, 2> unsafeUsesVec;
26842684
llvm::SmallVectorImpl<UnsafeUse> *unsafeUses = nullptr;
26852685
if (D->getASTContext().LangOpts.hasFeature(Feature::WarnUnsafe) &&
2686-
!D->isImplicit() && !D->isUnsafe()) {
2686+
!D->isImplicit() &&
2687+
D->getExplicitSafety() == ExplicitSafety::Unspecified) {
26872688
unsafeUses = &unsafeUsesVec;
26882689
}
26892690

lib/Sema/TypeCheckAttr.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
602602
void visitWeakLinkedAttr(WeakLinkedAttr *attr);
603603
void visitSILGenNameAttr(SILGenNameAttr *attr);
604604
void visitUnsafeAttr(UnsafeAttr *attr);
605+
void visitSafeAttr(SafeAttr *attr);
605606
void visitLifetimeAttr(LifetimeAttr *attr);
606607
void visitAddressableSelfAttr(AddressableSelfAttr *attr);
607608
void visitAddressableForDependenciesAttr(AddressableForDependenciesAttr *attr);
@@ -8003,6 +8004,13 @@ void AttributeChecker::visitUnsafeAttr(UnsafeAttr *attr) {
80038004
diagnoseAndRemoveAttr(attr, diag::unsafe_attr_disabled);
80048005
}
80058006

8007+
void AttributeChecker::visitSafeAttr(SafeAttr *attr) {
8008+
if (Ctx.LangOpts.hasFeature(Feature::AllowUnsafeAttribute))
8009+
return;
8010+
8011+
diagnoseAndRemoveAttr(attr, diag::unsafe_attr_disabled);
8012+
}
8013+
80068014
void AttributeChecker::visitLifetimeAttr(LifetimeAttr *attr) {}
80078015

80088016
void AttributeChecker::visitAddressableSelfAttr(AddressableSelfAttr *attr) {

0 commit comments

Comments
 (0)