Skip to content

Introduce @unsafe and the ability to prohibit use of unsafe entities #75413

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 27 additions & 12 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
// for the inline bitfields.
union { uint64_t OpaqueBits;

SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+1+1+1+1+1+1+1,
SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+1+1+1+1+1+1+1+1,
Kind : bitmax(NumDeclKindBits,8),

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

/// Whether this declaration was added to the surrounding
/// DeclContext of an active #if config clause.
EscapedFromIfConfig : 1,

/// Whether this declaration is syntactically scoped inside of
/// a local context, but should behave like a top-level
/// declaration for name lookup purposes. This is used by
Expand Down Expand Up @@ -404,7 +400,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {

/// True if we're in the common case where the SPIGroupsRequest
/// request returned an empty array of identifiers.
NoSPIGroups : 1
NoSPIGroups : 1,

/// True if we have computed whether this declaration is unsafe.
IsUnsafeComputed : 1,

/// True if this declaration has been determined to be "unsafe".
IsUnsafe : 1
);

SWIFT_INLINE_BITFIELD_FULL(PatternBindingDecl, Decl, 1+1+2+16,
Expand Down Expand Up @@ -857,6 +859,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
friend class ExpandPeerMacroRequest;
friend class GlobalActorAttributeRequest;
friend class SPIGroupsRequest;
friend class IsUnsafeRequest;

private:
llvm::PointerUnion<DeclContext *, ASTContext *> Context;
Expand Down Expand Up @@ -916,12 +919,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
Bits.Decl.Invalid = false;
Bits.Decl.Implicit = false;
Bits.Decl.FromClang = false;
Bits.Decl.EscapedFromIfConfig = false;
Bits.Decl.Hoisted = false;
Bits.Decl.LacksObjCInterfaceOrImplementation = false;
Bits.Decl.NoMemberAttributeMacros = false;
Bits.Decl.NoGlobalActorAttribute = false;
Bits.Decl.NoSPIGroups = false;
Bits.Decl.IsUnsafeComputed = false;
Bits.Decl.IsUnsafe = false;
}

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

public:
bool escapedFromIfConfig() const {
return Bits.Decl.EscapedFromIfConfig;
/// Whether this declaration is considered "unsafe", i.e., should not be
/// used in a "safe" dialect.
bool isUnsafe() const;

private:
bool isUnsafeComputed() const {
return Bits.Decl.IsUnsafeComputed;
}

bool isUnsafeRaw() const {
return Bits.Decl.IsUnsafe;
}

void setEscapedFromIfConfig(bool Escaped) {
Bits.Decl.EscapedFromIfConfig = Escaped;
void setUnsafe(bool value) {
assert(!Bits.Decl.IsUnsafeComputed);
Bits.Decl.IsUnsafe = value;
Bits.Decl.IsUnsafeComputed = true;
}

public:
bool getSemanticAttrsComputed() const {
return Bits.Decl.SemanticAttrsComputed;
}
Expand Down
8 changes: 7 additions & 1 deletion include/swift/AST/DeclAttr.def
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,13 @@ SIMPLE_DECL_ATTR(sensitive, Sensitive,
OnStruct | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
159)

LAST_DECL_ATTR(PreInverseGenerics)
SIMPLE_DECL_ATTR(unsafe, Unsafe,
OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType |
UserInaccessible |
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
160)

LAST_DECL_ATTR(Unsafe)

#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
28 changes: 28 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -7964,5 +7964,33 @@ NOTE(sending_function_result_with_sending_param_note, none,
"isolation domain through a result of an invocation of value",
())

//------------------------------------------------------------------------------
// MARK: Strict Safety Diagnostics
//------------------------------------------------------------------------------
ERROR(unsafe_attr_disabled,none,
"attribute requires '-enable-experimental-feature AllowUnsafeAttribute'", ())
WARNING(override_safe_withunsafe,none,
"override of safe %0 with unsafe %0", (DescriptiveDeclKind))
WARNING(witness_unsafe,none,
"unsafe %0 %1 cannot satisfy safe requirement",
(DescriptiveDeclKind, DeclName))
WARNING(type_witness_unsafe,none,
"unsafe type %0 cannot satisfy safe associated type %1",
(Type, DeclName))
WARNING(unchecked_conformance_is_unsafe,none,
"@unchecked conformance involves unsafe code", ())
WARNING(unowned_unsafe_is_unsafe,none,
"unowned(unsafe) involves unsafe code", ())
WARNING(nonisolated_unsafe_is_unsafe,none,
"nonisolated(unsafe) involves unsafe code", ())
WARNING(reference_to_unsafe_decl,none,
"%select{reference|call}0 to unsafe %kindbase1",
(bool, const ValueDecl *))
WARNING(reference_to_unsafe_typed_decl,none,
"%select{reference|call}0 to %kindbase1 involves unsafe type %2",
(bool, const ValueDecl *, Type))
NOTE(unsafe_decl_here,none,
"unsafe %kindbase0 declared here", (const ValueDecl *))

#define UNDEFINE_DIAGNOSTIC_MACROS
#include "DefineDiagnosticMacros.h"
18 changes: 18 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -5065,6 +5065,24 @@ class SuppressesConformanceRequest
bool isCached() const { return true; }
};

class IsUnsafeRequest
: public SimpleRequest<IsUnsafeRequest,
bool(Decl *decl),
RequestFlags::SeparatelyCached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

bool evaluate(Evaluator &evaluator, Decl *decl) const;

public:
bool isCached() const { return true; }
std::optional<bool> getCachedResult() const;
void cacheResult(bool value) const;
};

#define SWIFT_TYPEID_ZONE TypeChecker
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
#include "swift/Basic/DefineTypeIDZone.h"
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -593,3 +593,6 @@ SWIFT_REQUEST(TypeChecker, CaptureInfoRequest,
SWIFT_REQUEST(TypeChecker, ParamCaptureInfoRequest,
CaptureInfo(ParamDecl *),
SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsUnsafeRequest,
bool(Decl *),
SeparatelyCached, NoLocationInfo)
22 changes: 17 additions & 5 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,10 @@ class RecursiveTypeProperties {
/// Contains a PackArchetypeType. Also implies HasPrimaryArchetype.
HasPackArchetype = 0x20000,

Last_Property = HasPackArchetype
/// Whether this type contains an unsafe type.
IsUnsafe = 0x040000,

Last_Property = IsUnsafe
};
enum { BitWidth = countBitsUsed(Property::Last_Property) };

Expand Down Expand Up @@ -266,6 +269,8 @@ class RecursiveTypeProperties {

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

bool isUnsafe() const { return Bits & IsUnsafe; }

/// Does a type with these properties structurally contain a
/// parameterized existential type?
bool hasParameterizedExistential() const {
Expand Down Expand Up @@ -431,12 +436,12 @@ class alignas(1 << TypeAlignInBits) TypeBase
NumProtocols : 16
);

SWIFT_INLINE_BITFIELD_FULL(TypeVariableType, TypeBase, 7+29,
SWIFT_INLINE_BITFIELD_FULL(TypeVariableType, TypeBase, 7+28,
/// Type variable options.
Options : 7,
: NumPadBits,
/// The unique number assigned to this type variable.
ID : 29
ID : 28
);

SWIFT_INLINE_BITFIELD_FULL(ErrorUnionType, TypeBase, 32,
Expand Down Expand Up @@ -709,6 +714,11 @@ class alignas(1 << TypeAlignInBits) TypeBase
return getRecursiveProperties().hasPackArchetype();
}

/// Whether the type contains an @unsafe type in it anywhere.
bool isUnsafe() const {
return getRecursiveProperties().isUnsafe();
}

/// Determine whether the type involves a primary, pack or local archetype.
///
/// FIXME: Replace all remaining callers with a more precise check.
Expand Down Expand Up @@ -6637,7 +6647,8 @@ class PrimaryArchetypeType final : public ArchetypeType,
GenericEnvironment *GenericEnv,
Type InterfaceType,
ArrayRef<ProtocolDecl *> ConformsTo,
Type Superclass, LayoutConstraint Layout);
Type Superclass, LayoutConstraint Layout,
RecursiveTypeProperties Properties);
};
BEGIN_CAN_TYPE_WRAPPER(PrimaryArchetypeType, ArchetypeType)
END_CAN_TYPE_WRAPPER(PrimaryArchetypeType, ArchetypeType)
Expand Down Expand Up @@ -6918,7 +6929,8 @@ class PackArchetypeType final
private:
PackArchetypeType(const ASTContext &Ctx, GenericEnvironment *GenericEnv,
Type InterfaceType, ArrayRef<ProtocolDecl *> ConformsTo,
Type Superclass, LayoutConstraint Layout, PackShape Shape);
Type Superclass, LayoutConstraint Layout, PackShape Shape,
RecursiveTypeProperties properties);
};
BEGIN_CAN_TYPE_WRAPPER(PackArchetypeType, ArchetypeType)
END_CAN_TYPE_WRAPPER(PackArchetypeType, ArchetypeType)
Expand Down
7 changes: 7 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -396,11 +396,18 @@ EXPERIMENTAL_FEATURE(ReinitializeConsumeInMultiBlockDefer, false)

EXPERIMENTAL_FEATURE(SE427NoInferenceOnExtension, true)


EXPERIMENTAL_FEATURE(Extern, true)

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

/// Allow the @unsafe attribute.
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(AllowUnsafeAttribute, true)

/// Warn on use of unsafe constructs.
EXPERIMENTAL_FEATURE(WarnUnsafe, true)

#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
#undef EXPERIMENTAL_FEATURE
#undef UPCOMING_FEATURE
Expand Down
35 changes: 31 additions & 4 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3897,7 +3897,9 @@ get(GenericTypeDecl *TheDecl, Type Parent, const ASTContext &C) {
UnboundGenericType::Profile(ID, TheDecl, Parent);
void *InsertPos = nullptr;
RecursiveTypeProperties properties;
if (TheDecl->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
if (Parent) properties |= Parent->getRecursiveProperties();

auto arena = getArena(properties);

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

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

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

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

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

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

Expand Down Expand Up @@ -4325,20 +4331,39 @@ isAnyFunctionTypeCanonical(ArrayRef<AnyFunctionType::Param> params,
// always materializable.
static RecursiveTypeProperties
getGenericFunctionRecursiveProperties(ArrayRef<AnyFunctionType::Param> params,
Type result) {
static_assert(RecursiveTypeProperties::BitWidth == 18,
Type result, Type globalActor,
Type thrownError) {
static_assert(RecursiveTypeProperties::BitWidth == 19,
"revisit this if you add new recursive type properties");
RecursiveTypeProperties properties;

for (auto param : params) {
if (param.getPlainType()->getRecursiveProperties().hasError())
properties |= RecursiveTypeProperties::HasError;
if (param.getPlainType()->getRecursiveProperties().isUnsafe())
properties |= RecursiveTypeProperties::IsUnsafe;
}

if (result->getRecursiveProperties().hasDynamicSelf())
properties |= RecursiveTypeProperties::HasDynamicSelf;
if (result->getRecursiveProperties().hasError())
properties |= RecursiveTypeProperties::HasError;
if (result->getRecursiveProperties().isUnsafe())
properties |= RecursiveTypeProperties::IsUnsafe;

if (globalActor) {
if (globalActor->getRecursiveProperties().hasError())
properties |= RecursiveTypeProperties::HasError;
if (globalActor->getRecursiveProperties().isUnsafe())
properties |= RecursiveTypeProperties::IsUnsafe;
}

if (thrownError) {
if (thrownError->getRecursiveProperties().hasError())
properties |= RecursiveTypeProperties::HasError;
if (thrownError->getRecursiveProperties().isUnsafe())
properties |= RecursiveTypeProperties::IsUnsafe;
}

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

auto properties = getGenericFunctionRecursiveProperties(params, result);
auto properties = getGenericFunctionRecursiveProperties(
params, result, globalActor, thrownError);
auto funcTy = new (mem) GenericFunctionType(sig, params, result, info,
isCanonical ? &ctx : nullptr,
properties);
Expand Down Expand Up @@ -5044,7 +5070,7 @@ CanSILFunctionType SILFunctionType::get(
void *mem = ctx.Allocate(bytes, alignof(SILFunctionType));

RecursiveTypeProperties properties;
static_assert(RecursiveTypeProperties::BitWidth == 18,
static_assert(RecursiveTypeProperties::BitWidth == 19,
"revisit this if you add new recursive type properties");
for (auto &param : params)
properties |= param.getInterfaceType()->getRecursiveProperties();
Expand Down Expand Up @@ -5132,6 +5158,7 @@ OptionalType *OptionalType::get(Type base) {
ProtocolType *ProtocolType::get(ProtocolDecl *D, Type Parent,
const ASTContext &C) {
RecursiveTypeProperties properties;
if (D->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
if (Parent) properties |= Parent->getRecursiveProperties();
auto arena = getArena(properties);

Expand Down
9 changes: 9 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3056,6 +3056,15 @@ suppressingFeatureBitwiseCopyable2(PrintOptions &options,
options.ExcludeAttrList.resize(originalExcludeAttrCount);
}

static void
suppressingFeatureAllowUnsafeAttribute(PrintOptions &options,
llvm::function_ref<void()> action) {
unsigned originalExcludeAttrCount = options.ExcludeAttrList.size();
options.ExcludeAttrList.push_back(DeclAttrKind::Unsafe);
action();
options.ExcludeAttrList.resize(originalExcludeAttrCount);
}

/// Suppress the printing of a particular feature.
static void suppressingFeature(PrintOptions &options, Feature feature,
llvm::function_ref<void()> action) {
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,13 @@ bool Decl::preconcurrency() const {
return false;
}

bool Decl::isUnsafe() const {
return evaluateOrDefault(
getASTContext().evaluator,
IsUnsafeRequest{const_cast<Decl *>(this)},
false);
}

Type AbstractFunctionDecl::getThrownInterfaceType() const {
if (!getThrownTypeRepr())
return ThrownType.getType();
Expand Down
Loading