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

Lines changed: 35 additions & 6 deletions
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

Lines changed: 6 additions & 2 deletions
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

Lines changed: 8 additions & 3 deletions
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

Lines changed: 4 additions & 1 deletion
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

Lines changed: 0 additions & 18 deletions
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

Lines changed: 0 additions & 3 deletions
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

Lines changed: 12 additions & 6 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 11 additions & 1 deletion
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

Lines changed: 48 additions & 5 deletions
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 {

0 commit comments

Comments
 (0)