mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -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}];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -277,6 +277,15 @@ struct UnsafeContainingUnspecified {
|
||||
typealias A = Int
|
||||
|
||||
func getA() -> A { 0 }
|
||||
|
||||
@safe
|
||||
struct Y {
|
||||
var value: Int
|
||||
}
|
||||
|
||||
func f() {
|
||||
_ = Y(value: 5)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user