Skip to content

Commit 39f1e97

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 cccf6c1 commit 39f1e97

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
@@ -7974,6 +7974,9 @@ ERROR(override_safe_withunsafe,none,
79747974
ERROR(witness_unsafe,none,
79757975
"unsafe %0 %1 cannot satisfy safe requirement",
79767976
(DescriptiveDeclKind, DeclName))
7977+
ERROR(type_witness_unsafe,none,
7978+
"unsafe type %0 cannot satisfy safe associated type %1",
7979+
(Type, DeclName))
79777980
ERROR(unchecked_conformance_is_unsafe,none,
79787981
"@unchecked conformance involves unsafe code", ())
79797982
ERROR(unowned_unsafe_is_unsafe,none,
@@ -7983,6 +7986,11 @@ ERROR(nonisolated_unsafe_is_unsafe,none,
79837986
ERROR(reference_to_unsafe_decl,none,
79847987
"%select{reference|call}0 to unsafe %kindbase1",
79857988
(bool, const ValueDecl *))
7989+
ERROR(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 *))
79867994

79877995
#define UNDEFINE_DIAGNOSTIC_MACROS
79887996
#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
@@ -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/Type.cpp

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

3492+
/// Determine the recursive type properties for an archetype.
3493+
static RecursiveTypeProperties archetypeProperties(
3494+
RecursiveTypeProperties properties,
3495+
ArrayRef<ProtocolDecl *> conformsTo,
3496+
Type superclass
3497+
) {
3498+
for (auto proto : conformsTo) {
3499+
if (proto->isUnsafe()) {
3500+
properties |= RecursiveTypeProperties::IsUnsafe;
3501+
break;
3502+
}
3503+
}
3504+
3505+
if (superclass) properties |= superclass->getRecursiveProperties();
3506+
3507+
return properties;
3508+
}
3509+
34923510
PrimaryArchetypeType::PrimaryArchetypeType(const ASTContext &Ctx,
34933511
GenericEnvironment *GenericEnv,
34943512
Type InterfaceType,
34953513
ArrayRef<ProtocolDecl *> ConformsTo,
3496-
Type Superclass, LayoutConstraint Layout)
3497-
: ArchetypeType(TypeKind::PrimaryArchetype, Ctx,
3498-
RecursiveTypeProperties::HasPrimaryArchetype,
3514+
Type Superclass, LayoutConstraint Layout,
3515+
RecursiveTypeProperties Properties)
3516+
: ArchetypeType(TypeKind::PrimaryArchetype, Ctx, Properties,
34993517
InterfaceType, ConformsTo, Superclass, Layout, GenericEnv)
35003518
{
35013519
assert(!InterfaceType->isParameterPack());
@@ -3514,14 +3532,20 @@ PrimaryArchetypeType::getNew(const ASTContext &Ctx,
35143532
// Gather the set of protocol declarations to which this archetype conforms.
35153533
ProtocolType::canonicalizeProtocols(ConformsTo);
35163534

3535+
RecursiveTypeProperties Properties = archetypeProperties(
3536+
RecursiveTypeProperties::HasPrimaryArchetype,
3537+
ConformsTo, Superclass);
3538+
assert(!Properties.hasTypeVariable());
3539+
35173540
auto arena = AllocationArena::Permanent;
35183541
void *mem = Ctx.Allocate(
35193542
PrimaryArchetypeType::totalSizeToAlloc<ProtocolDecl *, Type, LayoutConstraint>(
35203543
ConformsTo.size(), Superclass ? 1 : 0, Layout ? 1 : 0),
35213544
alignof(PrimaryArchetypeType), arena);
35223545

35233546
return CanPrimaryArchetypeType(::new (mem) PrimaryArchetypeType(
3524-
Ctx, GenericEnv, InterfaceType, ConformsTo, Superclass, Layout));
3547+
Ctx, GenericEnv, InterfaceType, ConformsTo, Superclass, Layout,
3548+
Properties));
35253549
}
35263550

35273551
OpaqueTypeArchetypeType::OpaqueTypeArchetypeType(
@@ -3573,11 +3597,10 @@ UUID OpenedArchetypeType::getOpenedExistentialID() const {
35733597
PackArchetypeType::PackArchetypeType(
35743598
const ASTContext &Ctx, GenericEnvironment *GenericEnv, Type InterfaceType,
35753599
ArrayRef<ProtocolDecl *> ConformsTo, Type Superclass,
3576-
LayoutConstraint Layout, PackShape Shape)
3577-
: ArchetypeType(TypeKind::PackArchetype, Ctx,
3578-
RecursiveTypeProperties::HasPrimaryArchetype |
3579-
RecursiveTypeProperties::HasPackArchetype,
3580-
InterfaceType, ConformsTo, Superclass, Layout, GenericEnv) {
3600+
LayoutConstraint Layout, PackShape Shape,
3601+
RecursiveTypeProperties Properties
3602+
) : ArchetypeType(TypeKind::PackArchetype, Ctx, Properties,
3603+
InterfaceType, ConformsTo, Superclass, Layout, GenericEnv) {
35813604
assert(InterfaceType->isParameterPack());
35823605
*getTrailingObjects<PackShape>() = Shape;
35833606
}
@@ -3594,6 +3617,12 @@ PackArchetypeType::get(const ASTContext &Ctx,
35943617
// Gather the set of protocol declarations to which this archetype conforms.
35953618
ProtocolType::canonicalizeProtocols(ConformsTo);
35963619

3620+
RecursiveTypeProperties properties = archetypeProperties(
3621+
(RecursiveTypeProperties::HasPrimaryArchetype |
3622+
RecursiveTypeProperties::HasPackArchetype),
3623+
ConformsTo, Superclass);
3624+
assert(!properties.hasTypeVariable());
3625+
35973626
auto arena = AllocationArena::Permanent;
35983627
void *mem =
35993628
Ctx.Allocate(PackArchetypeType::totalSizeToAlloc<ProtocolDecl *, Type,
@@ -3604,7 +3633,7 @@ PackArchetypeType::get(const ASTContext &Ctx,
36043633

36053634
return CanPackArchetypeType(::new (mem) PackArchetypeType(
36063635
Ctx, GenericEnv, InterfaceType, ConformsTo, Superclass, Layout,
3607-
{ShapeType}));
3636+
{ShapeType}, properties));
36083637
}
36093638

36103639
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/ConformanceLookup.h"
3940
#include "swift/AST/Decl.h"
@@ -344,6 +345,22 @@ static void recordTypeWitness(NormalProtocolConformance *conformance,
344345
typeDecl = aliasDecl;
345346
}
346347

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

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"
@@ -3882,6 +3883,27 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
38823883
if (diagnoseDeclAvailability(D, R, call, Where, Flags))
38833884
return true;
38843885

3886+
// If the declaration itself is "safe" but we don't disallow unsafe uses,
3887+
// check whether it traffics in unsafe types.
3888+
ASTContext &ctx = D->getASTContext();
3889+
if (ctx.LangOpts.hasFeature(Feature::DisallowUnsafe) && !D->isUnsafe()) {
3890+
auto type = D->getInterfaceType();
3891+
if (auto subs = declRef.getSubstitutions())
3892+
type = type.subst(subs);
3893+
if (type->isUnsafe()) {
3894+
if (diagnoseUnsafeType(
3895+
ctx, R.Start, type,
3896+
[&](Type specificType) {
3897+
ctx.Diags.diagnose(
3898+
R.Start, diag::reference_to_unsafe_typed_decl,
3899+
call != nullptr && !isa<ParamDecl>(D), D,
3900+
specificType);
3901+
D->diagnose(diag::decl_declared_here, D);
3902+
}))
3903+
return true;
3904+
}
3905+
}
3906+
38853907
if (R.isValid()) {
38863908
if (diagnoseSubstitutionMapAvailability(R.Start, declRef.getSubstitutions(),
38873909
Where)) {

lib/Sema/TypeCheckType.cpp

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

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

0 commit comments

Comments
 (0)