[Strict memory safety] Nested types are safe/unsafe independent of their enclosing type

When determining whether a nested type is safe, don't consider whether
its enclosing type is safe. They're independent.
This commit is contained in:
Doug Gregor
2025-04-19 09:49:22 -07:00
parent fe6856726e
commit 8ec52c825c
3 changed files with 124 additions and 16 deletions

View File

@@ -4190,6 +4190,55 @@ void UnboundGenericType::Profile(llvm::FoldingSetNodeID &ID,
ID.AddPointer(Parent.getPointer());
}
/// The safety of a parent type does not have an impact on a nested type within
/// it. This produces the recursive properties of a given type that should
/// be propagated to a nested type, which won't include any "IsUnsafe" bit
/// determined based on the declaration itself.
static RecursiveTypeProperties getRecursivePropertiesAsParent(Type type) {
if (!type)
return RecursiveTypeProperties();
// We only need to do anything interesting at all for unsafe types.
auto properties = type->getRecursiveProperties();
if (!properties.isUnsafe())
return properties;
if (auto nominal = type->getAnyNominal()) {
// If the nominal wasn't itself unsafe, then we got the unsafety from
// something else (e.g., a generic argument), so it won't change.
if (nominal->getExplicitSafety() != ExplicitSafety::Unsafe)
return properties;
}
// Drop the "unsafe" bit. We have to recompute it without considering the
// enclosing nominal type.
properties = RecursiveTypeProperties(
properties.getBits() & ~static_cast<unsigned>(RecursiveTypeProperties::IsUnsafe));
// Check generic arguments of parent types.
while (type) {
// Merge from the generic arguments.
if (auto boundGeneric = type->getAs<BoundGenericType>()) {
for (auto genericArg : boundGeneric->getGenericArgs())
properties |= genericArg->getRecursiveProperties();
}
if (auto nominalOrBound = type->getAs<NominalOrBoundGenericNominalType>()) {
type = nominalOrBound->getParent();
continue;
}
if (auto unbound = type->getAs<UnboundGenericType>()) {
type = unbound->getParent();
continue;
}
break;
};
return properties;
}
UnboundGenericType *UnboundGenericType::
get(GenericTypeDecl *TheDecl, Type Parent, const ASTContext &C) {
llvm::FoldingSetNodeID ID;
@@ -4198,7 +4247,7 @@ get(GenericTypeDecl *TheDecl, Type Parent, const ASTContext &C) {
RecursiveTypeProperties properties;
if (TheDecl->getExplicitSafety() == ExplicitSafety::Unsafe)
properties |= RecursiveTypeProperties::IsUnsafe;
if (Parent) properties |= Parent->getRecursiveProperties();
properties |= getRecursivePropertiesAsParent(Parent);
auto arena = getArena(properties);
@@ -4252,7 +4301,7 @@ BoundGenericType *BoundGenericType::get(NominalTypeDecl *TheDecl,
RecursiveTypeProperties properties;
if (TheDecl->getExplicitSafety() == ExplicitSafety::Unsafe)
properties |= RecursiveTypeProperties::IsUnsafe;
if (Parent) properties |= Parent->getRecursiveProperties();
properties |= getRecursivePropertiesAsParent(Parent);
for (Type Arg : GenericArgs) {
properties |= Arg->getRecursiveProperties();
}
@@ -4335,7 +4384,7 @@ EnumType *EnumType::get(EnumDecl *D, Type Parent, const ASTContext &C) {
RecursiveTypeProperties properties;
if (D->getExplicitSafety() == ExplicitSafety::Unsafe)
properties |= RecursiveTypeProperties::IsUnsafe;
if (Parent) properties |= Parent->getRecursiveProperties();
properties |= getRecursivePropertiesAsParent(Parent);
auto arena = getArena(properties);
auto *&known = C.getImpl().getArena(arena).EnumTypes[{D, Parent}];
@@ -4353,7 +4402,7 @@ StructType *StructType::get(StructDecl *D, Type Parent, const ASTContext &C) {
RecursiveTypeProperties properties;
if (D->getExplicitSafety() == ExplicitSafety::Unsafe)
properties |= RecursiveTypeProperties::IsUnsafe;
if (Parent) properties |= Parent->getRecursiveProperties();
properties |= getRecursivePropertiesAsParent(Parent);
auto arena = getArena(properties);
auto *&known = C.getImpl().getArena(arena).StructTypes[{D, Parent}];
@@ -4371,7 +4420,7 @@ ClassType *ClassType::get(ClassDecl *D, Type Parent, const ASTContext &C) {
RecursiveTypeProperties properties;
if (D->getExplicitSafety() == ExplicitSafety::Unsafe)
properties |= RecursiveTypeProperties::IsUnsafe;
if (Parent) properties |= Parent->getRecursiveProperties();
properties |= getRecursivePropertiesAsParent(Parent);
auto arena = getArena(properties);
auto *&known = C.getImpl().getArena(arena).ClassTypes[{D, Parent}];
@@ -5538,7 +5587,7 @@ ProtocolType *ProtocolType::get(ProtocolDecl *D, Type Parent,
RecursiveTypeProperties properties;
if (D->getExplicitSafety() == ExplicitSafety::Unsafe)
properties |= RecursiveTypeProperties::IsUnsafe;
if (Parent) properties |= Parent->getRecursiveProperties();
properties |= getRecursivePropertiesAsParent(Parent);
auto arena = getArena(properties);
auto *&known = C.getImpl().getArena(arena).ProtocolTypes[{D, Parent}];

View File

@@ -327,7 +327,12 @@ bool swift::enumerateUnsafeUses(ArrayRef<ProtocolConformanceRef> conformances,
bool swift::enumerateUnsafeUses(SubstitutionMap subs,
SourceLoc loc,
llvm::function_ref<bool(UnsafeUse)> fn) {
// FIXME: Check replacement types?
// Replacement types.
for (auto replacementType : subs.getReplacementTypes()) {
if (replacementType->isUnsafe() &&
fn(UnsafeUse::forReferenceToUnsafe(nullptr, false, replacementType, loc)))
return true;
}
// Check conformances.
if (enumerateUnsafeUses(subs.getConformances(), loc, fn))
@@ -376,24 +381,69 @@ void swift::diagnoseUnsafeType(ASTContext &ctx, SourceLoc loc, Type type,
return;
// Look for a specific @unsafe nominal type along the way.
auto findSpecificUnsafeType = [](Type type) {
class Walker : public TypeWalker {
public:
Type specificType;
(void)type.findIf([&specificType](Type type) {
Action walkToTypePre(Type type) override {
if (specificType)
return Action::Stop;
// If this refers to a nominal type that is @unsafe, store that.
if (auto typeDecl = type->getAnyNominal()) {
if (typeDecl->getExplicitSafety() == ExplicitSafety::Unsafe) {
specificType = type;
return false;
return Action::Stop;
}
}
return false;
});
return specificType;
// Do not recurse into nominal types, because we do not want to visit
// their "parent" types.
if (isa<NominalOrBoundGenericNominalType>(type.getPointer()) ||
isa<UnboundGenericType>(type.getPointer())) {
// Recurse into the generic arguments. This operation is recursive,
// because we also need to see the generic arguments of parent types.
walkGenericArguments(type);
return Action::SkipNode;
}
return Action::Continue;
}
private:
/// Recursively walk the generic arguments of this type and its parent
/// types.
void walkGenericArguments(Type type) {
if (!type)
return;
// Walk the generic arguments.
if (auto boundGeneric = type->getAs<BoundGenericType>()) {
for (auto genericArg : boundGeneric->getGenericArgs())
genericArg.walk(*this);
}
if (auto nominalOrBound = type->getAs<NominalOrBoundGenericNominalType>())
return walkGenericArguments(nominalOrBound->getParent());
if (auto unbound = type->getAs<UnboundGenericType>())
return walkGenericArguments(unbound->getParent());
}
};
Type specificType = findSpecificUnsafeType(type);
if (!specificType)
specificType = findSpecificUnsafeType(type->getCanonicalType());
// Look for a canonical unsafe type.
Walker walker;
type->getCanonicalType().walk(walker);
Type specificType = walker.specificType;
// Look for an unsafe type in the non-canonical type, which is a better answer
// if we can find it.
walker.specificType = Type();
type.walk(walker);
if (specificType && walker.specificType &&
specificType->isEqual(walker.specificType))
specificType = walker.specificType;
diagnose(specificType ? specificType : type);
}

View File

@@ -277,6 +277,15 @@ struct UnsafeContainingUnspecified {
typealias A = Int
func getA() -> A { 0 }
@safe
struct Y {
var value: Int
}
func f() {
_ = Y(value: 5)
}
}