|
93 | 93 | #include "clang/Serialization/ObjectFilePCHContainerReader.h" |
94 | 94 | #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" |
95 | 95 | #include "clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h" |
| 96 | +#include "llvm/ADT/DenseSet.h" |
96 | 97 | #include "llvm/ADT/IntrusiveRefCntPtr.h" |
97 | 98 | #include "llvm/ADT/STLExtras.h" |
98 | 99 | #include "llvm/ADT/SmallVector.h" |
@@ -8769,132 +8770,128 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate( |
8769 | 8770 | return {CustomRefCountingOperationResult::tooManyFound, nullptr, name}; |
8770 | 8771 | } |
8771 | 8772 |
|
8772 | | -/// Check whether the given Clang type involves an unsafe type. |
8773 | | -static bool hasUnsafeType(Evaluator &evaluator, clang::QualType clangType) { |
8774 | | - // Handle pointers. |
8775 | | - auto pointeeType = clangType->getPointeeType(); |
8776 | | - if (!pointeeType.isNull()) { |
8777 | | - // Function pointers are okay. |
8778 | | - if (pointeeType->isFunctionType()) |
8779 | | - return false; |
8780 | | - |
8781 | | - // Pointers to record types are okay if they come in as foreign reference |
8782 | | - // types. |
8783 | | - if (auto *recordDecl = pointeeType->getAsRecordDecl()) { |
8784 | | - if (hasImportAsRefAttr(recordDecl)) |
8785 | | - return false; |
8786 | | - } |
8787 | | - |
8788 | | - // All other pointers are considered unsafe. |
8789 | | - return true; |
8790 | | - } |
8791 | | - |
8792 | | - // Handle records recursively. |
8793 | | - if (auto recordDecl = clangType->getAsTagDecl()) { |
8794 | | - // If we reached this point the types is not imported as a shared reference, |
8795 | | - // so we don't need to check the bases whether they are shared references. |
8796 | | - auto req = ClangDeclExplicitSafety({recordDecl, false}); |
8797 | | - if (evaluator.hasActiveRequest(req)) |
8798 | | - // Normally, using hasActiveRequest() to avoid cycles is an anti-pattern |
8799 | | - // because cycles should be treated as errors. However, cycles are allowed |
8800 | | - // in C++ template, e.g.: |
8801 | | - // |
8802 | | - // template <typename> class Foo { ... }; // throws away template arg |
8803 | | - // template <typename T> class Bar : Foo<Bar<T>> { ... }; |
8804 | | - // |
8805 | | - // A common use case of cyclic templates is the CRTP pattern. |
8806 | | - // |
8807 | | - // We need to avoid request cycles, so if there is already an active |
8808 | | - // request for this type, just assume it is not explicitly unsafe for now |
8809 | | - // (i.e., as if it were unspecified). |
8810 | | - return false; |
8811 | | - return evaluateOrDefault(evaluator, req, ExplicitSafety::Unspecified) == |
8812 | | - ExplicitSafety::Unsafe; |
8813 | | - } |
8814 | | - |
8815 | | - // Everything else is safe. |
8816 | | - return false; |
8817 | | -} |
8818 | | - |
8819 | 8773 | ExplicitSafety |
8820 | 8774 | ClangDeclExplicitSafety::evaluate(Evaluator &evaluator, |
8821 | 8775 | CxxDeclExplicitSafetyDescriptor desc) const { |
8822 | 8776 | // FIXME: Also similar to hasPointerInSubobjects |
8823 | 8777 | // FIXME: should probably also subsume IsSafeUseOfCxxDecl |
8824 | | - |
8825 | | - // Explicitly unsafe. |
8826 | | - auto decl = desc.decl; |
8827 | | - if (hasSwiftAttribute(decl, "unsafe")) |
8828 | | - return ExplicitSafety::Unsafe; |
8829 | | - |
8830 | | - // Explicitly safe. |
8831 | | - if (hasSwiftAttribute(decl, "safe")) |
8832 | | - return ExplicitSafety::Safe; |
8833 | | - |
8834 | | - // Shared references are considered safe. |
| 8778 | + |
8835 | 8779 | if (desc.isClass) |
8836 | | - return ExplicitSafety::Safe; |
8837 | | - |
8838 | | - // Enums are always safe. |
8839 | | - if (isa<clang::EnumDecl>(decl)) |
8840 | | - return ExplicitSafety::Safe; |
8841 | | - |
8842 | | - // If it's not a record, leave it unspecified. |
8843 | | - auto recordDecl = dyn_cast<clang::RecordDecl>(decl); |
8844 | | - if (!recordDecl) |
8845 | | - return ExplicitSafety::Unspecified; |
8846 | | - |
8847 | | - // Escapable and non-escapable annotations imply that the declaration is |
8848 | | - // safe. |
8849 | | - if (evaluateOrDefault( |
8850 | | - evaluator, |
8851 | | - ClangTypeEscapability({recordDecl->getTypeForDecl(), nullptr}), |
8852 | | - CxxEscapability::Unknown) != CxxEscapability::Unknown) |
8853 | | - return ExplicitSafety::Safe; |
8854 | | - |
8855 | | - // A template class is unsafe if any of its type arguments are unsafe. |
8856 | | - // Note that this does not rely on the record being defined. |
8857 | | - if (const auto *ctsd = |
8858 | | - dyn_cast<clang::ClassTemplateSpecializationDecl>(recordDecl)) { |
8859 | | - for (auto arg : ctsd->getTemplateArgs().asArray()) { |
8860 | | - switch (arg.getKind()) { |
8861 | | - case clang::TemplateArgument::Type: |
8862 | | - if (hasUnsafeType(evaluator, arg.getAsType())) |
8863 | | - return ExplicitSafety::Unsafe; |
8864 | | - break; |
8865 | | - case clang::TemplateArgument::Pack: |
8866 | | - for (auto pkArg : arg.getPackAsArray()) { |
8867 | | - if (pkArg.getKind() == clang::TemplateArgument::Type && |
8868 | | - hasUnsafeType(evaluator, pkArg.getAsType())) |
| 8780 | + // Safety for class types is handled a bit differently than other types. |
| 8781 | + // If it is not explicitly marked unsafe, it is always explicitly safe. |
| 8782 | + return hasSwiftAttribute(desc.decl, "unsafe") ? ExplicitSafety::Unsafe |
| 8783 | + : ExplicitSafety::Safe; |
| 8784 | + |
| 8785 | + // Clang record types are considered explicitly unsafe if any of their fields, |
| 8786 | + // base classes, and template type parameters are unsafe. We use a stack for |
| 8787 | + // this recursive traversal. |
| 8788 | + // |
| 8789 | + // Invariant: if any Decl in the stack is unsafe, then desc.decl is unsafe. |
| 8790 | + llvm::SmallVector<const clang::Decl *, 4> stack; |
| 8791 | + |
| 8792 | + // Keep track of which Decls we've seen to avoid cycles. |
| 8793 | + llvm::SmallDenseSet<const clang::Decl *, 4> seen; |
| 8794 | + |
| 8795 | + // Check whether a type is unsafe. This function may also push to the stack. |
| 8796 | + auto isUnsafe = [&](clang::QualType type) -> bool { |
| 8797 | + auto pointeeType = type->getPointeeType(); |
| 8798 | + if (!pointeeType.isNull()) { |
| 8799 | + if (pointeeType->isFunctionType()) |
| 8800 | + return false; // Function pointers are not unsafe |
| 8801 | + auto *recordDecl = pointeeType->getAsRecordDecl(); |
| 8802 | + if (recordDecl && hasImportAsRefAttr(recordDecl)) |
| 8803 | + return false; // Pointers are ok if imported as foreign reference types |
| 8804 | + return true; // All other pointers are considered unsafe. |
| 8805 | + } |
| 8806 | + if (auto *decl = type->getAsTagDecl()) { |
| 8807 | + // We need to check the safety of the TagDecl corresponding to this type |
| 8808 | + if (seen.insert(decl).second) |
| 8809 | + // Only visit decl if we have not seen it before, to avoid cycles |
| 8810 | + stack.push_back(decl); |
| 8811 | + } |
| 8812 | + return false; // This type does not look unsafe on its own |
| 8813 | + }; |
| 8814 | + |
| 8815 | + stack.push_back(desc.decl); |
| 8816 | + seen.insert(desc.decl); |
| 8817 | + while (!stack.empty()) { |
| 8818 | + const clang::Decl *decl = stack.back(); |
| 8819 | + stack.pop_back(); |
| 8820 | + |
| 8821 | + // Found unsafe; whether decl == desc.decl or not, desc.decl is unsafe |
| 8822 | + // (see invariant, above) |
| 8823 | + if (hasSwiftAttribute(decl, "unsafe")) |
| 8824 | + return ExplicitSafety::Unsafe; |
| 8825 | + |
| 8826 | + if (hasSwiftAttribute(decl, "safe")) |
| 8827 | + continue; |
| 8828 | + |
| 8829 | + // Enums are always safe |
| 8830 | + if (isa<clang::EnumDecl>(decl)) |
| 8831 | + continue; |
| 8832 | + |
| 8833 | + auto *recordDecl = dyn_cast<clang::RecordDecl>(decl); |
| 8834 | + if (!recordDecl) { |
| 8835 | + if (decl == desc.decl) |
| 8836 | + // If desc.decl is not a RecordDecl or EnumDecl, safety is unspecified. |
| 8837 | + return ExplicitSafety::Unspecified; |
| 8838 | + // If we encountered non-Record non-Enum decl during recursive traversal, |
| 8839 | + // we need to continue checking safety of other decls. |
| 8840 | + continue; |
| 8841 | + } |
| 8842 | + |
| 8843 | + // Escapability annotations imply that the declaration is safe |
| 8844 | + if (evaluateOrDefault( |
| 8845 | + evaluator, |
| 8846 | + ClangTypeEscapability({recordDecl->getTypeForDecl(), nullptr}), |
| 8847 | + CxxEscapability::Unknown) != CxxEscapability::Unknown) |
| 8848 | + continue; |
| 8849 | + |
| 8850 | + // A template class is unsafe if any of its type arguments are unsafe. |
| 8851 | + // Note that this does not rely on the record being defined. |
| 8852 | + if (auto *specDecl = |
| 8853 | + dyn_cast<clang::ClassTemplateSpecializationDecl>(recordDecl)) { |
| 8854 | + for (auto arg : specDecl->getTemplateArgs().asArray()) { |
| 8855 | + switch (arg.getKind()) { |
| 8856 | + case clang::TemplateArgument::Type: |
| 8857 | + if (isUnsafe(arg.getAsType())) |
8869 | 8858 | return ExplicitSafety::Unsafe; |
| 8859 | + break; |
| 8860 | + case clang::TemplateArgument::Pack: |
| 8861 | + for (auto pkArg : arg.getPackAsArray()) { |
| 8862 | + if (pkArg.getKind() == clang::TemplateArgument::Type && |
| 8863 | + isUnsafe(pkArg.getAsType())) |
| 8864 | + return ExplicitSafety::Unsafe; |
| 8865 | + } |
| 8866 | + break; |
| 8867 | + default: |
| 8868 | + continue; |
8870 | 8869 | } |
8871 | | - break; |
8872 | | - default: |
8873 | | - continue; |
8874 | 8870 | } |
8875 | 8871 | } |
8876 | | - } |
8877 | | - |
8878 | | - // If we don't have a definition, leave it unspecified. |
8879 | | - recordDecl = recordDecl->getDefinition(); |
8880 | | - if (!recordDecl) |
8881 | | - return ExplicitSafety::Unspecified; |
8882 | | - |
8883 | | - // If this is a C++ class, check its bases. |
8884 | | - if (auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(recordDecl)) { |
8885 | | - for (auto base : cxxRecordDecl->bases()) { |
8886 | | - if (hasUnsafeType(evaluator, base.getType())) |
| 8872 | + |
| 8873 | + recordDecl = recordDecl->getDefinition(); |
| 8874 | + if (!recordDecl) { |
| 8875 | + if (decl == desc.decl) |
| 8876 | + // If desc.decl doesn't have a definition, safety is unspecified. |
| 8877 | + return ExplicitSafety::Unspecified; |
| 8878 | + // If we encountered decl without definition during recursive traversal, |
| 8879 | + // we need to continue checking safety of other decls. |
| 8880 | + continue; |
| 8881 | + } |
| 8882 | + |
| 8883 | + if (auto *cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(recordDecl)) { |
| 8884 | + for (auto base : cxxRecordDecl->bases()) { |
| 8885 | + if (isUnsafe(base.getType())) |
| 8886 | + return ExplicitSafety::Unsafe; |
| 8887 | + } |
| 8888 | + } |
| 8889 | + |
| 8890 | + for (auto *field : recordDecl->fields()) { |
| 8891 | + if (isUnsafe(field->getType())) |
8887 | 8892 | return ExplicitSafety::Unsafe; |
8888 | 8893 | } |
8889 | 8894 | } |
8890 | | - |
8891 | | - // Check the fields. |
8892 | | - for (auto field : recordDecl->fields()) { |
8893 | | - if (hasUnsafeType(evaluator, field->getType())) |
8894 | | - return ExplicitSafety::Unsafe; |
8895 | | - } |
8896 | | - |
8897 | | - // Okay, call it safe. |
8898 | 8895 | return ExplicitSafety::Safe; |
8899 | 8896 | } |
8900 | 8897 |
|
|
0 commit comments