Skip to content

Commit d9f7d94

Browse files
authored
Merge pull request swiftlang#75413 from DougGregor/unsafe
Introduce `@unsafe` and the ability to prohibit use of unsafe entities
2 parents a9d483a + 5412a8a commit d9f7d94

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+593
-38
lines changed

include/swift/AST/Decl.h

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
357357
// for the inline bitfields.
358358
union { uint64_t OpaqueBits;
359359

360-
SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+1+1+1+1+1+1+1,
360+
SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+1+1+1+1+1+1+1+1,
361361
Kind : bitmax(NumDeclKindBits,8),
362362

363363
/// Whether this declaration is invalid.
@@ -372,10 +372,6 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
372372
/// Use getClangNode() to retrieve the corresponding Clang AST.
373373
FromClang : 1,
374374

375-
/// Whether this declaration was added to the surrounding
376-
/// DeclContext of an active #if config clause.
377-
EscapedFromIfConfig : 1,
378-
379375
/// Whether this declaration is syntactically scoped inside of
380376
/// a local context, but should behave like a top-level
381377
/// declaration for name lookup purposes. This is used by
@@ -404,7 +400,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
404400

405401
/// True if we're in the common case where the SPIGroupsRequest
406402
/// request returned an empty array of identifiers.
407-
NoSPIGroups : 1
403+
NoSPIGroups : 1,
404+
405+
/// True if we have computed whether this declaration is unsafe.
406+
IsUnsafeComputed : 1,
407+
408+
/// True if this declaration has been determined to be "unsafe".
409+
IsUnsafe : 1
408410
);
409411

410412
SWIFT_INLINE_BITFIELD_FULL(PatternBindingDecl, Decl, 1+1+2+16,
@@ -857,6 +859,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
857859
friend class ExpandPeerMacroRequest;
858860
friend class GlobalActorAttributeRequest;
859861
friend class SPIGroupsRequest;
862+
friend class IsUnsafeRequest;
860863

861864
private:
862865
llvm::PointerUnion<DeclContext *, ASTContext *> Context;
@@ -916,12 +919,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
916919
Bits.Decl.Invalid = false;
917920
Bits.Decl.Implicit = false;
918921
Bits.Decl.FromClang = false;
919-
Bits.Decl.EscapedFromIfConfig = false;
920922
Bits.Decl.Hoisted = false;
921923
Bits.Decl.LacksObjCInterfaceOrImplementation = false;
922924
Bits.Decl.NoMemberAttributeMacros = false;
923925
Bits.Decl.NoGlobalActorAttribute = false;
924926
Bits.Decl.NoSPIGroups = false;
927+
Bits.Decl.IsUnsafeComputed = false;
928+
Bits.Decl.IsUnsafe = false;
925929
}
926930

927931
/// Get the Clang node associated with this declaration.
@@ -1180,15 +1184,26 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
11801184
/// Whether this declaration predates the introduction of concurrency.
11811185
bool preconcurrency() const;
11821186

1183-
public:
1184-
bool escapedFromIfConfig() const {
1185-
return Bits.Decl.EscapedFromIfConfig;
1187+
/// Whether this declaration is considered "unsafe", i.e., should not be
1188+
/// used in a "safe" dialect.
1189+
bool isUnsafe() const;
1190+
1191+
private:
1192+
bool isUnsafeComputed() const {
1193+
return Bits.Decl.IsUnsafeComputed;
1194+
}
1195+
1196+
bool isUnsafeRaw() const {
1197+
return Bits.Decl.IsUnsafe;
11861198
}
11871199

1188-
void setEscapedFromIfConfig(bool Escaped) {
1189-
Bits.Decl.EscapedFromIfConfig = Escaped;
1200+
void setUnsafe(bool value) {
1201+
assert(!Bits.Decl.IsUnsafeComputed);
1202+
Bits.Decl.IsUnsafe = value;
1203+
Bits.Decl.IsUnsafeComputed = true;
11901204
}
11911205

1206+
public:
11921207
bool getSemanticAttrsComputed() const {
11931208
return Bits.Decl.SemanticAttrsComputed;
11941209
}

include/swift/AST/DeclAttr.def

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,13 @@ SIMPLE_DECL_ATTR(sensitive, Sensitive,
500500
OnStruct | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
501501
159)
502502

503-
LAST_DECL_ATTR(PreInverseGenerics)
503+
SIMPLE_DECL_ATTR(unsafe, Unsafe,
504+
OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType |
505+
UserInaccessible |
506+
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
507+
160)
508+
509+
LAST_DECL_ATTR(Unsafe)
504510

505511
#undef DECL_ATTR_ALIAS
506512
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/DiagnosticsSema.def

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7964,5 +7964,33 @@ NOTE(sending_function_result_with_sending_param_note, none,
79647964
"isolation domain through a result of an invocation of value",
79657965
())
79667966

7967+
//------------------------------------------------------------------------------
7968+
// MARK: Strict Safety Diagnostics
7969+
//------------------------------------------------------------------------------
7970+
ERROR(unsafe_attr_disabled,none,
7971+
"attribute requires '-enable-experimental-feature AllowUnsafeAttribute'", ())
7972+
WARNING(override_safe_withunsafe,none,
7973+
"override of safe %0 with unsafe %0", (DescriptiveDeclKind))
7974+
WARNING(witness_unsafe,none,
7975+
"unsafe %0 %1 cannot satisfy safe requirement",
7976+
(DescriptiveDeclKind, DeclName))
7977+
WARNING(type_witness_unsafe,none,
7978+
"unsafe type %0 cannot satisfy safe associated type %1",
7979+
(Type, DeclName))
7980+
WARNING(unchecked_conformance_is_unsafe,none,
7981+
"@unchecked conformance involves unsafe code", ())
7982+
WARNING(unowned_unsafe_is_unsafe,none,
7983+
"unowned(unsafe) involves unsafe code", ())
7984+
WARNING(nonisolated_unsafe_is_unsafe,none,
7985+
"nonisolated(unsafe) involves unsafe code", ())
7986+
WARNING(reference_to_unsafe_decl,none,
7987+
"%select{reference|call}0 to unsafe %kindbase1",
7988+
(bool, const ValueDecl *))
7989+
WARNING(reference_to_unsafe_typed_decl,none,
7990+
"%select{reference|call}0 to %kindbase1 involves unsafe type %2",
7991+
(bool, const ValueDecl *, Type))
7992+
NOTE(unsafe_decl_here,none,
7993+
"unsafe %kindbase0 declared here", (const ValueDecl *))
7994+
79677995
#define UNDEFINE_DIAGNOSTIC_MACROS
79687996
#include "DefineDiagnosticMacros.h"

include/swift/AST/TypeCheckRequests.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5065,6 +5065,24 @@ class SuppressesConformanceRequest
50655065
bool isCached() const { return true; }
50665066
};
50675067

5068+
class IsUnsafeRequest
5069+
: public SimpleRequest<IsUnsafeRequest,
5070+
bool(Decl *decl),
5071+
RequestFlags::SeparatelyCached> {
5072+
public:
5073+
using SimpleRequest::SimpleRequest;
5074+
5075+
private:
5076+
friend SimpleRequest;
5077+
5078+
bool evaluate(Evaluator &evaluator, Decl *decl) const;
5079+
5080+
public:
5081+
bool isCached() const { return true; }
5082+
std::optional<bool> getCachedResult() const;
5083+
void cacheResult(bool value) const;
5084+
};
5085+
50685086
#define SWIFT_TYPEID_ZONE TypeChecker
50695087
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
50705088
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,3 +593,6 @@ SWIFT_REQUEST(TypeChecker, CaptureInfoRequest,
593593
SWIFT_REQUEST(TypeChecker, ParamCaptureInfoRequest,
594594
CaptureInfo(ParamDecl *),
595595
SeparatelyCached, NoLocationInfo)
596+
SWIFT_REQUEST(TypeChecker, IsUnsafeRequest,
597+
bool(Decl *),
598+
SeparatelyCached, NoLocationInfo)

include/swift/AST/Types.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,10 @@ class RecursiveTypeProperties {
185185
/// Contains a PackArchetypeType. Also implies HasPrimaryArchetype.
186186
HasPackArchetype = 0x20000,
187187

188-
Last_Property = HasPackArchetype
188+
/// Whether this type contains an unsafe type.
189+
IsUnsafe = 0x040000,
190+
191+
Last_Property = IsUnsafe
189192
};
190193
enum { BitWidth = countBitsUsed(Property::Last_Property) };
191194

@@ -266,6 +269,8 @@ class RecursiveTypeProperties {
266269

267270
bool hasPackArchetype() const { return Bits & HasPackArchetype; }
268271

272+
bool isUnsafe() const { return Bits & IsUnsafe; }
273+
269274
/// Does a type with these properties structurally contain a
270275
/// parameterized existential type?
271276
bool hasParameterizedExistential() const {
@@ -431,12 +436,12 @@ class alignas(1 << TypeAlignInBits) TypeBase
431436
NumProtocols : 16
432437
);
433438

434-
SWIFT_INLINE_BITFIELD_FULL(TypeVariableType, TypeBase, 7+29,
439+
SWIFT_INLINE_BITFIELD_FULL(TypeVariableType, TypeBase, 7+28,
435440
/// Type variable options.
436441
Options : 7,
437442
: NumPadBits,
438443
/// The unique number assigned to this type variable.
439-
ID : 29
444+
ID : 28
440445
);
441446

442447
SWIFT_INLINE_BITFIELD_FULL(ErrorUnionType, TypeBase, 32,
@@ -709,6 +714,11 @@ class alignas(1 << TypeAlignInBits) TypeBase
709714
return getRecursiveProperties().hasPackArchetype();
710715
}
711716

717+
/// Whether the type contains an @unsafe type in it anywhere.
718+
bool isUnsafe() const {
719+
return getRecursiveProperties().isUnsafe();
720+
}
721+
712722
/// Determine whether the type involves a primary, pack or local archetype.
713723
///
714724
/// FIXME: Replace all remaining callers with a more precise check.
@@ -6637,7 +6647,8 @@ class PrimaryArchetypeType final : public ArchetypeType,
66376647
GenericEnvironment *GenericEnv,
66386648
Type InterfaceType,
66396649
ArrayRef<ProtocolDecl *> ConformsTo,
6640-
Type Superclass, LayoutConstraint Layout);
6650+
Type Superclass, LayoutConstraint Layout,
6651+
RecursiveTypeProperties Properties);
66416652
};
66426653
BEGIN_CAN_TYPE_WRAPPER(PrimaryArchetypeType, ArchetypeType)
66436654
END_CAN_TYPE_WRAPPER(PrimaryArchetypeType, ArchetypeType)
@@ -6918,7 +6929,8 @@ class PackArchetypeType final
69186929
private:
69196930
PackArchetypeType(const ASTContext &Ctx, GenericEnvironment *GenericEnv,
69206931
Type InterfaceType, ArrayRef<ProtocolDecl *> ConformsTo,
6921-
Type Superclass, LayoutConstraint Layout, PackShape Shape);
6932+
Type Superclass, LayoutConstraint Layout, PackShape Shape,
6933+
RecursiveTypeProperties properties);
69226934
};
69236935
BEGIN_CAN_TYPE_WRAPPER(PackArchetypeType, ArchetypeType)
69246936
END_CAN_TYPE_WRAPPER(PackArchetypeType, ArchetypeType)

include/swift/Basic/Features.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,11 +396,18 @@ EXPERIMENTAL_FEATURE(ReinitializeConsumeInMultiBlockDefer, false)
396396

397397
EXPERIMENTAL_FEATURE(SE427NoInferenceOnExtension, true)
398398

399+
399400
EXPERIMENTAL_FEATURE(Extern, true)
400401

401402
// Enable trailing comma for comma-separated lists.
402403
EXPERIMENTAL_FEATURE(TrailingComma, false)
403404

405+
/// Allow the @unsafe attribute.
406+
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(AllowUnsafeAttribute, true)
407+
408+
/// Warn on use of unsafe constructs.
409+
EXPERIMENTAL_FEATURE(WarnUnsafe, true)
410+
404411
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
405412
#undef EXPERIMENTAL_FEATURE
406413
#undef UPCOMING_FEATURE

lib/AST/ASTContext.cpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3897,7 +3897,9 @@ get(GenericTypeDecl *TheDecl, Type Parent, const ASTContext &C) {
38973897
UnboundGenericType::Profile(ID, TheDecl, Parent);
38983898
void *InsertPos = nullptr;
38993899
RecursiveTypeProperties properties;
3900+
if (TheDecl->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
39003901
if (Parent) properties |= Parent->getRecursiveProperties();
3902+
39013903
auto arena = getArena(properties);
39023904

39033905
if (auto unbound = C.getImpl().getArena(arena).UnboundGenericTypes
@@ -3948,6 +3950,7 @@ BoundGenericType *BoundGenericType::get(NominalTypeDecl *TheDecl,
39483950
llvm::FoldingSetNodeID ID;
39493951
BoundGenericType::Profile(ID, TheDecl, Parent, GenericArgs);
39503952
RecursiveTypeProperties properties;
3953+
if (TheDecl->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
39513954
if (Parent) properties |= Parent->getRecursiveProperties();
39523955
for (Type Arg : GenericArgs) {
39533956
properties |= Arg->getRecursiveProperties();
@@ -4029,6 +4032,7 @@ EnumType::EnumType(EnumDecl *TheDecl, Type Parent, const ASTContext &C,
40294032

40304033
EnumType *EnumType::get(EnumDecl *D, Type Parent, const ASTContext &C) {
40314034
RecursiveTypeProperties properties;
4035+
if (D->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
40324036
if (Parent) properties |= Parent->getRecursiveProperties();
40334037
auto arena = getArena(properties);
40344038

@@ -4045,6 +4049,7 @@ StructType::StructType(StructDecl *TheDecl, Type Parent, const ASTContext &C,
40454049

40464050
StructType *StructType::get(StructDecl *D, Type Parent, const ASTContext &C) {
40474051
RecursiveTypeProperties properties;
4052+
if (D->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
40484053
if (Parent) properties |= Parent->getRecursiveProperties();
40494054
auto arena = getArena(properties);
40504055

@@ -4061,6 +4066,7 @@ ClassType::ClassType(ClassDecl *TheDecl, Type Parent, const ASTContext &C,
40614066

40624067
ClassType *ClassType::get(ClassDecl *D, Type Parent, const ASTContext &C) {
40634068
RecursiveTypeProperties properties;
4069+
if (D->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
40644070
if (Parent) properties |= Parent->getRecursiveProperties();
40654071
auto arena = getArena(properties);
40664072

@@ -4325,20 +4331,39 @@ isAnyFunctionTypeCanonical(ArrayRef<AnyFunctionType::Param> params,
43254331
// always materializable.
43264332
static RecursiveTypeProperties
43274333
getGenericFunctionRecursiveProperties(ArrayRef<AnyFunctionType::Param> params,
4328-
Type result) {
4329-
static_assert(RecursiveTypeProperties::BitWidth == 18,
4334+
Type result, Type globalActor,
4335+
Type thrownError) {
4336+
static_assert(RecursiveTypeProperties::BitWidth == 19,
43304337
"revisit this if you add new recursive type properties");
43314338
RecursiveTypeProperties properties;
43324339

43334340
for (auto param : params) {
43344341
if (param.getPlainType()->getRecursiveProperties().hasError())
43354342
properties |= RecursiveTypeProperties::HasError;
4343+
if (param.getPlainType()->getRecursiveProperties().isUnsafe())
4344+
properties |= RecursiveTypeProperties::IsUnsafe;
43364345
}
43374346

43384347
if (result->getRecursiveProperties().hasDynamicSelf())
43394348
properties |= RecursiveTypeProperties::HasDynamicSelf;
43404349
if (result->getRecursiveProperties().hasError())
43414350
properties |= RecursiveTypeProperties::HasError;
4351+
if (result->getRecursiveProperties().isUnsafe())
4352+
properties |= RecursiveTypeProperties::IsUnsafe;
4353+
4354+
if (globalActor) {
4355+
if (globalActor->getRecursiveProperties().hasError())
4356+
properties |= RecursiveTypeProperties::HasError;
4357+
if (globalActor->getRecursiveProperties().isUnsafe())
4358+
properties |= RecursiveTypeProperties::IsUnsafe;
4359+
}
4360+
4361+
if (thrownError) {
4362+
if (thrownError->getRecursiveProperties().hasError())
4363+
properties |= RecursiveTypeProperties::HasError;
4364+
if (thrownError->getRecursiveProperties().isUnsafe())
4365+
properties |= RecursiveTypeProperties::IsUnsafe;
4366+
}
43424367

43434368
return properties;
43444369
}
@@ -4671,7 +4696,8 @@ GenericFunctionType *GenericFunctionType::get(GenericSignature sig,
46714696
hasLifetimeDependenceInfo ? numLifetimeDependencies : 0);
46724697
void *mem = ctx.Allocate(allocSize, alignof(GenericFunctionType));
46734698

4674-
auto properties = getGenericFunctionRecursiveProperties(params, result);
4699+
auto properties = getGenericFunctionRecursiveProperties(
4700+
params, result, globalActor, thrownError);
46754701
auto funcTy = new (mem) GenericFunctionType(sig, params, result, info,
46764702
isCanonical ? &ctx : nullptr,
46774703
properties);
@@ -5044,7 +5070,7 @@ CanSILFunctionType SILFunctionType::get(
50445070
void *mem = ctx.Allocate(bytes, alignof(SILFunctionType));
50455071

50465072
RecursiveTypeProperties properties;
5047-
static_assert(RecursiveTypeProperties::BitWidth == 18,
5073+
static_assert(RecursiveTypeProperties::BitWidth == 19,
50485074
"revisit this if you add new recursive type properties");
50495075
for (auto &param : params)
50505076
properties |= param.getInterfaceType()->getRecursiveProperties();
@@ -5132,6 +5158,7 @@ OptionalType *OptionalType::get(Type base) {
51325158
ProtocolType *ProtocolType::get(ProtocolDecl *D, Type Parent,
51335159
const ASTContext &C) {
51345160
RecursiveTypeProperties properties;
5161+
if (D->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
51355162
if (Parent) properties |= Parent->getRecursiveProperties();
51365163
auto arena = getArena(properties);
51375164

lib/AST/ASTPrinter.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3056,6 +3056,15 @@ suppressingFeatureBitwiseCopyable2(PrintOptions &options,
30563056
options.ExcludeAttrList.resize(originalExcludeAttrCount);
30573057
}
30583058

3059+
static void
3060+
suppressingFeatureAllowUnsafeAttribute(PrintOptions &options,
3061+
llvm::function_ref<void()> action) {
3062+
unsigned originalExcludeAttrCount = options.ExcludeAttrList.size();
3063+
options.ExcludeAttrList.push_back(DeclAttrKind::Unsafe);
3064+
action();
3065+
options.ExcludeAttrList.resize(originalExcludeAttrCount);
3066+
}
3067+
30593068
/// Suppress the printing of a particular feature.
30603069
static void suppressingFeature(PrintOptions &options, Feature feature,
30613070
llvm::function_ref<void()> action) {

lib/AST/Decl.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,13 @@ bool Decl::preconcurrency() const {
10151015
return false;
10161016
}
10171017

1018+
bool Decl::isUnsafe() const {
1019+
return evaluateOrDefault(
1020+
getASTContext().evaluator,
1021+
IsUnsafeRequest{const_cast<Decl *>(this)},
1022+
false);
1023+
}
1024+
10181025
Type AbstractFunctionDecl::getThrownInterfaceType() const {
10191026
if (!getThrownTypeRepr())
10201027
return ThrownType.getType();

0 commit comments

Comments
 (0)