Skip to content

Commit 0612986

Browse files
committed
[Safe mode] Diagnose uses of declarations involving unsafe types
When referencing a declaration, check whether any of the types in that reference are unsafe. This can diagnose cases where the original declaration either wasn't actually unsafe, or is being provided with unsafe types via generics.
1 parent bf661d9 commit 0612986

File tree

10 files changed

+209
-24
lines changed

10 files changed

+209
-24
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7922,6 +7922,9 @@ ERROR(override_safe_withunsafe,none,
79227922
ERROR(witness_unsafe,none,
79237923
"unsafe %0 %1 cannot satisfy safe requirement",
79247924
(DescriptiveDeclKind, DeclName))
7925+
ERROR(type_witness_unsafe,none,
7926+
"unsafe type %0 cannot satisfy safe associated type %1",
7927+
(Type, DeclName))
79257928
ERROR(unchecked_conformance_is_unsafe,none,
79267929
"@unchecked conformance involves unsafe code", ())
79277930
ERROR(unowned_unsafe_is_unsafe,none,
@@ -7931,6 +7934,11 @@ ERROR(nonisolated_unsafe_is_unsafe,none,
79317934
ERROR(reference_to_unsafe_decl,none,
79327935
"%select{reference|call}0 to unsafe %kindbase1",
79337936
(bool, const ValueDecl *))
7937+
ERROR(reference_to_unsafe_typed_decl,none,
7938+
"%select{reference|call}0 to %kindbase1 involves unsafe type %2",
7939+
(bool, const ValueDecl *, Type))
7940+
NOTE(unsafe_decl_here,none,
7941+
"unsafe %kindbase0 declared here", (const ValueDecl *))
79347942

79357943
#define UNDEFINE_DIAGNOSTIC_MACROS
79367944
#include "DefineDiagnosticMacros.h"

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)

lib/AST/ASTContext.cpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3852,7 +3852,9 @@ get(GenericTypeDecl *TheDecl, Type Parent, const ASTContext &C) {
38523852
UnboundGenericType::Profile(ID, TheDecl, Parent);
38533853
void *InsertPos = nullptr;
38543854
RecursiveTypeProperties properties;
3855+
if (TheDecl->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
38553856
if (Parent) properties |= Parent->getRecursiveProperties();
3857+
38563858
auto arena = getArena(properties);
38573859

38583860
if (auto unbound = C.getImpl().getArena(arena).UnboundGenericTypes
@@ -3903,6 +3905,7 @@ BoundGenericType *BoundGenericType::get(NominalTypeDecl *TheDecl,
39033905
llvm::FoldingSetNodeID ID;
39043906
BoundGenericType::Profile(ID, TheDecl, Parent, GenericArgs);
39053907
RecursiveTypeProperties properties;
3908+
if (TheDecl->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
39063909
if (Parent) properties |= Parent->getRecursiveProperties();
39073910
for (Type Arg : GenericArgs) {
39083911
properties |= Arg->getRecursiveProperties();
@@ -3984,6 +3987,7 @@ EnumType::EnumType(EnumDecl *TheDecl, Type Parent, const ASTContext &C,
39843987

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

@@ -4000,6 +4004,7 @@ StructType::StructType(StructDecl *TheDecl, Type Parent, const ASTContext &C,
40004004

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

@@ -4016,6 +4021,7 @@ ClassType::ClassType(ClassDecl *TheDecl, Type Parent, const ASTContext &C,
40164021

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

@@ -4280,20 +4286,39 @@ isAnyFunctionTypeCanonical(ArrayRef<AnyFunctionType::Param> params,
42804286
// always materializable.
42814287
static RecursiveTypeProperties
42824288
getGenericFunctionRecursiveProperties(ArrayRef<AnyFunctionType::Param> params,
4283-
Type result) {
4284-
static_assert(RecursiveTypeProperties::BitWidth == 18,
4289+
Type result, Type globalActor,
4290+
Type thrownError) {
4291+
static_assert(RecursiveTypeProperties::BitWidth == 19,
42854292
"revisit this if you add new recursive type properties");
42864293
RecursiveTypeProperties properties;
42874294

42884295
for (auto param : params) {
42894296
if (param.getPlainType()->getRecursiveProperties().hasError())
42904297
properties |= RecursiveTypeProperties::HasError;
4298+
if (param.getPlainType()->getRecursiveProperties().isUnsafe())
4299+
properties |= RecursiveTypeProperties::IsUnsafe;
42914300
}
42924301

42934302
if (result->getRecursiveProperties().hasDynamicSelf())
42944303
properties |= RecursiveTypeProperties::HasDynamicSelf;
42954304
if (result->getRecursiveProperties().hasError())
42964305
properties |= RecursiveTypeProperties::HasError;
4306+
if (result->getRecursiveProperties().isUnsafe())
4307+
properties |= RecursiveTypeProperties::IsUnsafe;
4308+
4309+
if (globalActor) {
4310+
if (globalActor->getRecursiveProperties().hasError())
4311+
properties |= RecursiveTypeProperties::HasError;
4312+
if (globalActor->getRecursiveProperties().isUnsafe())
4313+
properties |= RecursiveTypeProperties::IsUnsafe;
4314+
}
4315+
4316+
if (thrownError) {
4317+
if (thrownError->getRecursiveProperties().hasError())
4318+
properties |= RecursiveTypeProperties::HasError;
4319+
if (thrownError->getRecursiveProperties().isUnsafe())
4320+
properties |= RecursiveTypeProperties::IsUnsafe;
4321+
}
42974322

42984323
return properties;
42994324
}
@@ -4626,7 +4651,8 @@ GenericFunctionType *GenericFunctionType::get(GenericSignature sig,
46264651
hasLifetimeDependenceInfo ? numLifetimeDependencies : 0);
46274652
void *mem = ctx.Allocate(allocSize, alignof(GenericFunctionType));
46284653

4629-
auto properties = getGenericFunctionRecursiveProperties(params, result);
4654+
auto properties = getGenericFunctionRecursiveProperties(
4655+
params, result, globalActor, thrownError);
46304656
auto funcTy = new (mem) GenericFunctionType(sig, params, result, info,
46314657
isCanonical ? &ctx : nullptr,
46324658
properties);
@@ -4999,7 +5025,7 @@ CanSILFunctionType SILFunctionType::get(
49995025
void *mem = ctx.Allocate(bytes, alignof(SILFunctionType));
50005026

50015027
RecursiveTypeProperties properties;
5002-
static_assert(RecursiveTypeProperties::BitWidth == 18,
5028+
static_assert(RecursiveTypeProperties::BitWidth == 19,
50035029
"revisit this if you add new recursive type properties");
50045030
for (auto &param : params)
50055031
properties |= param.getInterfaceType()->getRecursiveProperties();
@@ -5087,6 +5113,7 @@ OptionalType *OptionalType::get(Type base) {
50875113
ProtocolType *ProtocolType::get(ProtocolDecl *D, Type Parent,
50885114
const ASTContext &C) {
50895115
RecursiveTypeProperties properties;
5116+
if (D->isUnsafe()) properties |= RecursiveTypeProperties::IsUnsafe;
50905117
if (Parent) properties |= Parent->getRecursiveProperties();
50915118
auto arena = getArena(properties);
50925119

lib/AST/Type.cpp

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3485,13 +3485,31 @@ std::string ArchetypeType::getFullName() const {
34853485
return InterfaceType.getString();
34863486
}
34873487

3488+
/// Determine the recursive type properties for an archetype.
3489+
static RecursiveTypeProperties archetypeProperties(
3490+
RecursiveTypeProperties properties,
3491+
ArrayRef<ProtocolDecl *> conformsTo,
3492+
Type superclass
3493+
) {
3494+
for (auto proto : conformsTo) {
3495+
if (proto->isUnsafe()) {
3496+
properties |= RecursiveTypeProperties::IsUnsafe;
3497+
break;
3498+
}
3499+
}
3500+
3501+
if (superclass) properties |= superclass->getRecursiveProperties();
3502+
3503+
return properties;
3504+
}
3505+
34883506
PrimaryArchetypeType::PrimaryArchetypeType(const ASTContext &Ctx,
34893507
GenericEnvironment *GenericEnv,
34903508
Type InterfaceType,
34913509
ArrayRef<ProtocolDecl *> ConformsTo,
3492-
Type Superclass, LayoutConstraint Layout)
3493-
: ArchetypeType(TypeKind::PrimaryArchetype, Ctx,
3494-
RecursiveTypeProperties::HasPrimaryArchetype,
3510+
Type Superclass, LayoutConstraint Layout,
3511+
RecursiveTypeProperties Properties)
3512+
: ArchetypeType(TypeKind::PrimaryArchetype, Ctx, Properties,
34953513
InterfaceType, ConformsTo, Superclass, Layout, GenericEnv)
34963514
{
34973515
assert(!InterfaceType->isParameterPack());
@@ -3510,14 +3528,20 @@ PrimaryArchetypeType::getNew(const ASTContext &Ctx,
35103528
// Gather the set of protocol declarations to which this archetype conforms.
35113529
ProtocolType::canonicalizeProtocols(ConformsTo);
35123530

3531+
RecursiveTypeProperties Properties = archetypeProperties(
3532+
RecursiveTypeProperties::HasPrimaryArchetype,
3533+
ConformsTo, Superclass);
3534+
assert(!Properties.hasTypeVariable());
3535+
35133536
auto arena = AllocationArena::Permanent;
35143537
void *mem = Ctx.Allocate(
35153538
PrimaryArchetypeType::totalSizeToAlloc<ProtocolDecl *, Type, LayoutConstraint>(
35163539
ConformsTo.size(), Superclass ? 1 : 0, Layout ? 1 : 0),
35173540
alignof(PrimaryArchetypeType), arena);
35183541

35193542
return CanPrimaryArchetypeType(::new (mem) PrimaryArchetypeType(
3520-
Ctx, GenericEnv, InterfaceType, ConformsTo, Superclass, Layout));
3543+
Ctx, GenericEnv, InterfaceType, ConformsTo, Superclass, Layout,
3544+
Properties));
35213545
}
35223546

35233547
OpaqueTypeArchetypeType::OpaqueTypeArchetypeType(
@@ -3569,11 +3593,10 @@ UUID OpenedArchetypeType::getOpenedExistentialID() const {
35693593
PackArchetypeType::PackArchetypeType(
35703594
const ASTContext &Ctx, GenericEnvironment *GenericEnv, Type InterfaceType,
35713595
ArrayRef<ProtocolDecl *> ConformsTo, Type Superclass,
3572-
LayoutConstraint Layout, PackShape Shape)
3573-
: ArchetypeType(TypeKind::PackArchetype, Ctx,
3574-
RecursiveTypeProperties::HasPrimaryArchetype |
3575-
RecursiveTypeProperties::HasPackArchetype,
3576-
InterfaceType, ConformsTo, Superclass, Layout, GenericEnv) {
3596+
LayoutConstraint Layout, PackShape Shape,
3597+
RecursiveTypeProperties Properties
3598+
) : ArchetypeType(TypeKind::PackArchetype, Ctx, Properties,
3599+
InterfaceType, ConformsTo, Superclass, Layout, GenericEnv) {
35773600
assert(InterfaceType->isParameterPack());
35783601
*getTrailingObjects<PackShape>() = Shape;
35793602
}
@@ -3590,6 +3613,12 @@ PackArchetypeType::get(const ASTContext &Ctx,
35903613
// Gather the set of protocol declarations to which this archetype conforms.
35913614
ProtocolType::canonicalizeProtocols(ConformsTo);
35923615

3616+
RecursiveTypeProperties properties = archetypeProperties(
3617+
(RecursiveTypeProperties::HasPrimaryArchetype |
3618+
RecursiveTypeProperties::HasPackArchetype),
3619+
ConformsTo, Superclass);
3620+
assert(!properties.hasTypeVariable());
3621+
35933622
auto arena = AllocationArena::Permanent;
35943623
void *mem =
35953624
Ctx.Allocate(PackArchetypeType::totalSizeToAlloc<ProtocolDecl *, Type,
@@ -3600,7 +3629,7 @@ PackArchetypeType::get(const ASTContext &Ctx,
36003629

36013630
return CanPackArchetypeType(::new (mem) PackArchetypeType(
36023631
Ctx, GenericEnv, InterfaceType, ConformsTo, Superclass, Layout,
3603-
{ShapeType}));
3632+
{ShapeType}, properties));
36043633
}
36053634

36063635
CanType PackArchetypeType::getReducedShape() {

lib/Sema/AssociatedTypeInference.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "DerivedConformances.h"
3535
#include "TypeAccessScopeChecker.h"
3636
#include "TypeChecker.h"
37+
#include "TypeCheckType.h"
3738

3839
#include "swift/AST/Decl.h"
3940
#include "swift/AST/GenericSignature.h"
@@ -343,6 +344,22 @@ static void recordTypeWitness(NormalProtocolConformance *conformance,
343344
typeDecl = aliasDecl;
344345
}
345346

347+
// If we're disallowing unsafe code, check for an unsafe type witness.
348+
if (ctx.LangOpts.hasFeature(Feature::DisallowUnsafe) &&
349+
!assocType->isUnsafe() && type->isUnsafe()) {
350+
SourceLoc loc = typeDecl->getLoc();
351+
if (loc.isInvalid())
352+
loc = conformance->getLoc();
353+
diagnoseUnsafeType(ctx,
354+
loc,
355+
type,
356+
[&](Type specificType) {
357+
ctx.Diags.diagnose(
358+
loc, diag::type_witness_unsafe, specificType, assocType->getName());
359+
assocType->diagnose(diag::decl_declared_here, assocType);
360+
});
361+
}
362+
346363
// Record the type witness.
347364
conformance->setTypeWitness(assocType, type, typeDecl);
348365

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "MiscDiagnostics.h"
1919
#include "TypeCheckConcurrency.h"
2020
#include "TypeCheckObjC.h"
21+
#include "TypeCheckType.h"
2122
#include "TypeChecker.h"
2223
#include "swift/AST/ASTWalker.h"
2324
#include "swift/AST/ClangModuleLoader.h"
@@ -3873,6 +3874,27 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
38733874
if (diagnoseDeclAvailability(D, R, call, Where, Flags))
38743875
return true;
38753876

3877+
// If the declaration itself is "safe" but we don't disallow unsafe uses,
3878+
// check whether it traffics in unsafe types.
3879+
ASTContext &ctx = D->getASTContext();
3880+
if (ctx.LangOpts.hasFeature(Feature::DisallowUnsafe) && !D->isUnsafe()) {
3881+
auto type = D->getInterfaceType();
3882+
if (auto subs = declRef.getSubstitutions())
3883+
type = type.subst(subs);
3884+
if (type->isUnsafe()) {
3885+
if (diagnoseUnsafeType(
3886+
ctx, R.Start, type,
3887+
[&](Type specificType) {
3888+
ctx.Diags.diagnose(
3889+
R.Start, diag::reference_to_unsafe_typed_decl,
3890+
call != nullptr && !isa<ParamDecl>(D), D,
3891+
specificType);
3892+
D->diagnose(diag::decl_declared_here, D);
3893+
}))
3894+
return true;
3895+
}
3896+
}
3897+
38763898
if (R.isValid()) {
38773899
if (diagnoseSubstitutionMapAvailability(R.Start, declRef.getSubstitutions(),
38783900
Where)) {

lib/Sema/TypeCheckType.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6418,3 +6418,35 @@ Type ExplicitCaughtTypeRequest::evaluate(
64186418

64196419
llvm_unreachable("Unhandled catch node");
64206420
}
6421+
6422+
bool swift::diagnoseUnsafeType(ASTContext &ctx, SourceLoc loc, Type type,
6423+
llvm::function_ref<void(Type)> diagnose) {
6424+
if (!ctx.LangOpts.hasFeature(Feature::DisallowUnsafe))
6425+
return false;
6426+
6427+
if (!type->isUnsafe())
6428+
return false;
6429+
6430+
// Look for a specific @unsafe nominal type.
6431+
Type specificType;
6432+
type.findIf([&specificType](Type type) {
6433+
if (auto typeDecl = type->getAnyNominal()) {
6434+
if (typeDecl->isUnsafe()) {
6435+
specificType = type;
6436+
return true;
6437+
}
6438+
}
6439+
6440+
return false;
6441+
});
6442+
6443+
diagnose(specificType ? specificType : type);
6444+
6445+
if (specificType) {
6446+
if (auto specificTypeDecl = specificType->getAnyNominal()) {
6447+
specificTypeDecl->diagnose(diag::unsafe_decl_here, specificTypeDecl);
6448+
}
6449+
}
6450+
6451+
return true;
6452+
}

0 commit comments

Comments
 (0)