Files
swift-mirror/include/swift/ClangImporter/ClangImporterRequests.h
Gabor Horvath ddacdf416d [cxx-interop] Shared references are considered safe
This patch makes sure we don't get warnings in strict memory safe mode
when using shared references. Those types are reference counted so we
are unlikely to run into lifetime errors.

rdar://151039766
2025-06-13 15:41:51 +01:00

650 lines
22 KiB
C++

//===--- ClangImporterRequests.h - Clang Importer Requests ------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines clang-importer requests.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_CLANG_IMPORTER_REQUESTS_H
#define SWIFT_CLANG_IMPORTER_REQUESTS_H
#include "swift/AST/ASTTypeIDs.h"
#include "swift/AST/EvaluatorDependencies.h"
#include "swift/AST/Identifier.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/SimpleRequest.h"
#include "swift/Basic/Statistic.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/TinyPtrVector.h"
namespace swift {
class Decl;
class DeclName;
class EnumDecl;
enum class ExplicitSafety;
/// The input type for a clang direct lookup request.
struct ClangDirectLookupDescriptor final {
Decl *decl;
const clang::Decl *clangDecl;
DeclName name;
ClangDirectLookupDescriptor(Decl *decl, const clang::Decl *clangDecl,
DeclName name)
: decl(decl), clangDecl(clangDecl), name(name) {}
friend llvm::hash_code hash_value(const ClangDirectLookupDescriptor &desc) {
return llvm::hash_combine(desc.name, desc.decl, desc.clangDecl);
}
friend bool operator==(const ClangDirectLookupDescriptor &lhs,
const ClangDirectLookupDescriptor &rhs) {
return lhs.name == rhs.name && lhs.decl == rhs.decl &&
lhs.clangDecl == rhs.clangDecl;
}
friend bool operator!=(const ClangDirectLookupDescriptor &lhs,
const ClangDirectLookupDescriptor &rhs) {
return !(lhs == rhs);
}
};
void simple_display(llvm::raw_ostream &out,
const ClangDirectLookupDescriptor &desc);
SourceLoc extractNearestSourceLoc(const ClangDirectLookupDescriptor &desc);
/// This matches SwiftLookupTable::SingleEntry;
using SingleEntry = llvm::PointerUnion<clang::NamedDecl *, clang::MacroInfo *,
clang::ModuleMacro *>;
/// Uses the appropriate SwiftLookupTable to find a set of clang decls given
/// their name.
class ClangDirectLookupRequest
: public SimpleRequest<ClangDirectLookupRequest,
SmallVector<SingleEntry, 4>(
ClangDirectLookupDescriptor),
RequestFlags::Uncached> {
public:
using SimpleRequest::SimpleRequest;
private:
friend SimpleRequest;
// Evaluation.
SmallVector<SingleEntry, 4> evaluate(Evaluator &evaluator,
ClangDirectLookupDescriptor desc) const;
};
/// The input type for a namespace member lookup request.
struct CXXNamespaceMemberLookupDescriptor final {
EnumDecl *namespaceDecl;
DeclName name;
CXXNamespaceMemberLookupDescriptor(EnumDecl *namespaceDecl, DeclName name)
: namespaceDecl(namespaceDecl), name(name) {
assert(isa<clang::NamespaceDecl>(namespaceDecl->getClangDecl()));
}
friend llvm::hash_code
hash_value(const CXXNamespaceMemberLookupDescriptor &desc) {
return llvm::hash_combine(desc.name, desc.namespaceDecl);
}
friend bool operator==(const CXXNamespaceMemberLookupDescriptor &lhs,
const CXXNamespaceMemberLookupDescriptor &rhs) {
return lhs.name == rhs.name && lhs.namespaceDecl == rhs.namespaceDecl;
}
friend bool operator!=(const CXXNamespaceMemberLookupDescriptor &lhs,
const CXXNamespaceMemberLookupDescriptor &rhs) {
return !(lhs == rhs);
}
};
void simple_display(llvm::raw_ostream &out,
const CXXNamespaceMemberLookupDescriptor &desc);
SourceLoc
extractNearestSourceLoc(const CXXNamespaceMemberLookupDescriptor &desc);
/// Uses ClangDirectLookup to find a named member inside of the given namespace.
class CXXNamespaceMemberLookup
: public SimpleRequest<CXXNamespaceMemberLookup,
TinyPtrVector<ValueDecl *>(
CXXNamespaceMemberLookupDescriptor),
RequestFlags::Uncached> {
public:
using SimpleRequest::SimpleRequest;
private:
friend SimpleRequest;
// Evaluation.
TinyPtrVector<ValueDecl *>
evaluate(Evaluator &evaluator, CXXNamespaceMemberLookupDescriptor desc) const;
};
/// The input type for a record member lookup request.
///
/// These lookups may be requested recursively in the case of inheritance,
/// for which we separately keep track of the derived class where we started
/// looking (startDecl) and the access level for the current inheritance.
struct ClangRecordMemberLookupDescriptor final {
NominalTypeDecl *recordDecl; // Where we are currently looking
NominalTypeDecl *inheritingDecl; // Where we started looking from
DeclName name; // What we are looking for
ClangInheritanceInfo inheritance;
ClangRecordMemberLookupDescriptor(NominalTypeDecl *recordDecl, DeclName name)
: recordDecl(recordDecl), inheritingDecl(recordDecl), name(name),
inheritance() {
assert(isa<clang::RecordDecl>(recordDecl->getClangDecl()));
}
friend llvm::hash_code
hash_value(const ClangRecordMemberLookupDescriptor &desc) {
return llvm::hash_combine(desc.name, desc.recordDecl, desc.inheritingDecl,
desc.inheritance);
}
friend bool operator==(const ClangRecordMemberLookupDescriptor &lhs,
const ClangRecordMemberLookupDescriptor &rhs) {
return lhs.name == rhs.name && lhs.recordDecl == rhs.recordDecl &&
lhs.inheritingDecl == rhs.inheritingDecl &&
lhs.inheritance == rhs.inheritance;
}
friend bool operator!=(const ClangRecordMemberLookupDescriptor &lhs,
const ClangRecordMemberLookupDescriptor &rhs) {
return !(lhs == rhs);
}
private:
friend class ClangRecordMemberLookup;
// This private constructor should only be used in ClangRecordMemberLookup,
// for recursively traversing base classes that inheritingDecl inherites from.
ClangRecordMemberLookupDescriptor(NominalTypeDecl *recordDecl, DeclName name,
NominalTypeDecl *inheritingDecl,
ClangInheritanceInfo inheritance)
: recordDecl(recordDecl), inheritingDecl(inheritingDecl), name(name),
inheritance(inheritance) {
assert(isa<clang::RecordDecl>(recordDecl->getClangDecl()));
assert(isa<clang::CXXRecordDecl>(inheritingDecl->getClangDecl()));
assert(inheritance.isInheriting() &&
"recursive calls should indicate inheritance");
assert(recordDecl != inheritingDecl &&
"recursive calls should lookup elsewhere");
}
};
void simple_display(llvm::raw_ostream &out,
const ClangRecordMemberLookupDescriptor &desc);
SourceLoc
extractNearestSourceLoc(const ClangRecordMemberLookupDescriptor &desc);
/// Uses ClangDirectLookup to find a named member inside of the given record.
class ClangRecordMemberLookup
: public SimpleRequest<ClangRecordMemberLookup,
TinyPtrVector<ValueDecl *>(
ClangRecordMemberLookupDescriptor),
RequestFlags::Uncached> {
public:
using SimpleRequest::SimpleRequest;
private:
friend SimpleRequest;
// Evaluation.
TinyPtrVector<ValueDecl *>
evaluate(Evaluator &evaluator, ClangRecordMemberLookupDescriptor desc) const;
};
/// The input type for a clang category lookup request.
struct ClangCategoryLookupDescriptor final {
const ClassDecl *classDecl;
Identifier categoryName;
ClangCategoryLookupDescriptor(const ClassDecl *classDecl,
Identifier categoryName)
: classDecl(classDecl), categoryName(categoryName) {}
friend llvm::hash_code hash_value(const ClangCategoryLookupDescriptor &desc) {
return llvm::hash_combine(desc.classDecl, desc.categoryName);
}
friend bool operator==(const ClangCategoryLookupDescriptor &lhs,
const ClangCategoryLookupDescriptor &rhs) {
return lhs.classDecl == rhs.classDecl
&& lhs.categoryName == rhs.categoryName;
}
friend bool operator!=(const ClangCategoryLookupDescriptor &lhs,
const ClangCategoryLookupDescriptor &rhs) {
return !(lhs == rhs);
}
};
void simple_display(llvm::raw_ostream &out,
const ClangCategoryLookupDescriptor &desc);
SourceLoc extractNearestSourceLoc(const ClangCategoryLookupDescriptor &desc);
/// Given a Swift class, find the imported Swift decl(s) representing the
/// \c \@interface with the given category name. An empty \c categoryName
/// represents the main interface for the class.
///
/// That is, this request will return one of:
///
/// \li a single \c swift::ExtensionDecl backed by a \c clang::ObjCCategoryDecl
/// \li a \c swift::ClassDecl backed by a \c clang::ObjCInterfaceDecl, plus
/// zero or more \c swift::ExtensionDecl s backed by
/// \c clang::ObjCCategoryDecl s (representing ObjC class extensions).
/// \li an empty list if the class is not imported from Clang or it does not
/// have a category by that name.
class ClangCategoryLookupRequest
: public SimpleRequest<ClangCategoryLookupRequest,
TinyPtrVector<Decl *>(ClangCategoryLookupDescriptor),
RequestFlags::Uncached> {
public:
using SimpleRequest::SimpleRequest;
private:
friend SimpleRequest;
// Evaluation.
TinyPtrVector<Decl *> evaluate(Evaluator &evaluator,
ClangCategoryLookupDescriptor desc) const;
};
/// Links an \c \@_objcImplementation decl to the imported declaration(s) that
/// it implements.
///
/// There is usually a 1:1 correspondence between interfaces and
/// implementations, except that a class's main implementation implements
/// both its main interface and any class extension interfaces. In this
/// situation, the main class is always the first decl in \c interfaceDecls.
struct ObjCInterfaceAndImplementation final {
llvm::TinyPtrVector<Decl *> interfaceDecls;
Decl *implementationDecl;
ObjCInterfaceAndImplementation(llvm::TinyPtrVector<Decl *> interfaceDecls,
Decl *implementationDecl)
: interfaceDecls(interfaceDecls), implementationDecl(implementationDecl)
{
assert(!interfaceDecls.empty() && implementationDecl &&
"interface and implementation are both non-null");
}
ObjCInterfaceAndImplementation()
: interfaceDecls(), implementationDecl(nullptr) {}
bool empty() const {
return interfaceDecls.empty();
}
friend llvm::hash_code
hash_value(const ObjCInterfaceAndImplementation &pair) {
return hash_combine(llvm::hash_combine_range(pair.interfaceDecls.begin(),
pair.interfaceDecls.end()),
pair.implementationDecl);
}
friend bool operator==(const ObjCInterfaceAndImplementation &lhs,
const ObjCInterfaceAndImplementation &rhs) {
return lhs.interfaceDecls == rhs.interfaceDecls
&& lhs.implementationDecl == rhs.implementationDecl;
}
friend bool operator!=(const ObjCInterfaceAndImplementation &lhs,
const ObjCInterfaceAndImplementation &rhs) {
return !(lhs == rhs);
}
};
void simple_display(llvm::raw_ostream &out,
const ObjCInterfaceAndImplementation &desc);
SourceLoc extractNearestSourceLoc(const ObjCInterfaceAndImplementation &desc);
/// Given a \c Decl , determine if it is an implementation with separate
/// interfaces imported from ObjC (or vice versa) and if so, return all of the
/// declarations involved in this relationship. Otherwise return an empty value.
///
/// We perform this lookup in both directions using a single request because
/// we want to cache the relationship on both sides to avoid duplicating work.
class ObjCInterfaceAndImplementationRequest
: public SimpleRequest<ObjCInterfaceAndImplementationRequest,
ObjCInterfaceAndImplementation(Decl *),
RequestFlags::SeparatelyCached> {
public:
using SimpleRequest::SimpleRequest;
private:
friend SimpleRequest;
// Evaluation.
ObjCInterfaceAndImplementation
evaluate(Evaluator &evaluator, Decl *decl) const;
public:
// Separate caching.
bool isCached() const { return true; }
std::optional<ObjCInterfaceAndImplementation> getCachedResult() const;
void cacheResult(ObjCInterfaceAndImplementation value) const;
};
enum class CxxRecordSemanticsKind {
Trivial,
Owned,
MoveOnly,
Reference,
Iterator,
// A record that is either not copyable/movable or not destructible.
MissingLifetimeOperation,
// A record that has no copy and no move operations
UnavailableConstructors,
// A C++ record that represents a Swift class type exposed to C++ from Swift.
SwiftClassType
};
struct CxxRecordSemanticsDescriptor final {
const clang::RecordDecl *decl;
ASTContext &ctx;
ClangImporter::Implementation *importerImpl;
CxxRecordSemanticsDescriptor(const clang::RecordDecl *decl, ASTContext &ctx,
ClangImporter::Implementation *importerImpl)
: decl(decl), ctx(ctx), importerImpl(importerImpl) {}
friend llvm::hash_code hash_value(const CxxRecordSemanticsDescriptor &desc) {
return llvm::hash_combine(desc.decl);
}
friend bool operator==(const CxxRecordSemanticsDescriptor &lhs,
const CxxRecordSemanticsDescriptor &rhs) {
return lhs.decl == rhs.decl;
}
friend bool operator!=(const CxxRecordSemanticsDescriptor &lhs,
const CxxRecordSemanticsDescriptor &rhs) {
return !(lhs == rhs);
}
};
void simple_display(llvm::raw_ostream &out, CxxRecordSemanticsDescriptor desc);
SourceLoc extractNearestSourceLoc(CxxRecordSemanticsDescriptor desc);
/// What pattern does this C++ API fit? Uses attributes such as
/// import_owned and import_reference to determine the pattern.
///
/// Do not evaluate this request before importing has started. For example, it
/// is OK to invoke this request when importing a decl, but it is not OK to
/// evaluate this request when importing names. This is because when importing
/// names, Clang sema has not yet defined implicit special members, so the
/// results will be flakey/incorrect.
class CxxRecordSemantics
: public SimpleRequest<CxxRecordSemantics,
CxxRecordSemanticsKind(CxxRecordSemanticsDescriptor),
RequestFlags::Uncached> {
public:
using SimpleRequest::SimpleRequest;
// Source location
SourceLoc getNearestLoc() const { return SourceLoc(); };
private:
friend SimpleRequest;
// Evaluation.
CxxRecordSemanticsKind evaluate(Evaluator &evaluator,
CxxRecordSemanticsDescriptor) const;
};
/// Does this C++ record represent a Swift type.
class CxxRecordAsSwiftType
: public SimpleRequest<CxxRecordAsSwiftType,
ValueDecl *(CxxRecordSemanticsDescriptor),
RequestFlags::Uncached> {
public:
using SimpleRequest::SimpleRequest;
// Source location
SourceLoc getNearestLoc() const { return SourceLoc(); };
private:
friend SimpleRequest;
ValueDecl *evaluate(Evaluator &evaluator, CxxRecordSemanticsDescriptor) const;
};
struct SafeUseOfCxxDeclDescriptor final {
const clang::Decl *decl;
SafeUseOfCxxDeclDescriptor(const clang::Decl *decl) : decl(decl) {}
friend llvm::hash_code hash_value(const SafeUseOfCxxDeclDescriptor &desc) {
return llvm::hash_combine(desc.decl);
}
friend bool operator==(const SafeUseOfCxxDeclDescriptor &lhs,
const SafeUseOfCxxDeclDescriptor &rhs) {
return lhs.decl == rhs.decl;
}
friend bool operator!=(const SafeUseOfCxxDeclDescriptor &lhs,
const SafeUseOfCxxDeclDescriptor &rhs) {
return !(lhs == rhs);
}
};
void simple_display(llvm::raw_ostream &out, SafeUseOfCxxDeclDescriptor desc);
SourceLoc extractNearestSourceLoc(SafeUseOfCxxDeclDescriptor desc);
class IsSafeUseOfCxxDecl
: public SimpleRequest<IsSafeUseOfCxxDecl, bool(SafeUseOfCxxDeclDescriptor),
RequestFlags::Uncached> {
public:
using SimpleRequest::SimpleRequest;
// Source location
SourceLoc getNearestLoc() const { return SourceLoc(); };
private:
friend SimpleRequest;
// Evaluation.
bool evaluate(Evaluator &evaluator, SafeUseOfCxxDeclDescriptor desc) const;
};
enum class CustomRefCountingOperationKind { retain, release };
struct CustomRefCountingOperationDescriptor final {
const ClassDecl *decl;
CustomRefCountingOperationKind kind;
CustomRefCountingOperationDescriptor(const ClassDecl *decl,
CustomRefCountingOperationKind kind)
: decl(decl), kind(kind) {}
friend llvm::hash_code
hash_value(const CustomRefCountingOperationDescriptor &desc) {
return llvm::hash_combine(desc.decl, desc.kind);
}
friend bool operator==(const CustomRefCountingOperationDescriptor &lhs,
const CustomRefCountingOperationDescriptor &rhs) {
return lhs.decl == rhs.decl && lhs.kind == rhs.kind;
}
friend bool operator!=(const CustomRefCountingOperationDescriptor &lhs,
const CustomRefCountingOperationDescriptor &rhs) {
return !(lhs == rhs);
}
};
void simple_display(llvm::raw_ostream &out,
CustomRefCountingOperationDescriptor desc);
SourceLoc extractNearestSourceLoc(CustomRefCountingOperationDescriptor desc);
struct CustomRefCountingOperationResult {
enum CustomRefCountingOperationResultKind {
noAttribute,
tooManyAttributes,
immortal,
notFound,
tooManyFound,
foundOperation
};
CustomRefCountingOperationResultKind kind;
ValueDecl *operation;
std::string name;
};
class CustomRefCountingOperation
: public SimpleRequest<CustomRefCountingOperation,
CustomRefCountingOperationResult(
CustomRefCountingOperationDescriptor),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;
// Caching
bool isCached() const { return true; }
// Source location
SourceLoc getNearestLoc() const { return SourceLoc(); };
private:
friend SimpleRequest;
// Evaluation.
CustomRefCountingOperationResult
evaluate(Evaluator &evaluator,
CustomRefCountingOperationDescriptor desc) const;
};
enum class CxxEscapability { Escapable, NonEscapable, Unknown };
struct EscapabilityLookupDescriptor final {
const clang::Type *type;
ClangImporter::Implementation *impl;
// Only explicitly ~Escapable annotated types are considered ~Escapable.
// This is for backward compatibility, so we continue to import aggregates
// containing pointers as Escapable types.
bool annotationOnly = true;
friend llvm::hash_code hash_value(const EscapabilityLookupDescriptor &desc) {
return llvm::hash_combine(desc.type);
}
friend bool operator==(const EscapabilityLookupDescriptor &lhs,
const EscapabilityLookupDescriptor &rhs) {
return lhs.type == rhs.type && lhs.annotationOnly == rhs.annotationOnly;
}
friend bool operator!=(const EscapabilityLookupDescriptor &lhs,
const EscapabilityLookupDescriptor &rhs) {
return !(lhs == rhs);
}
};
class ClangTypeEscapability
: public SimpleRequest<ClangTypeEscapability,
CxxEscapability(EscapabilityLookupDescriptor),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;
bool isCached() const { return true; }
private:
friend SimpleRequest;
CxxEscapability evaluate(Evaluator &evaluator,
EscapabilityLookupDescriptor desc) const;
};
void simple_display(llvm::raw_ostream &out, EscapabilityLookupDescriptor desc);
SourceLoc extractNearestSourceLoc(EscapabilityLookupDescriptor desc);
struct CxxDeclExplicitSafetyDescriptor final {
const clang::Decl *decl;
bool isClass;
CxxDeclExplicitSafetyDescriptor(const clang::Decl *decl, bool isClass)
: decl(decl), isClass(isClass) {}
friend llvm::hash_code
hash_value(const CxxDeclExplicitSafetyDescriptor &desc) {
return llvm::hash_combine(desc.decl, desc.isClass);
}
friend bool operator==(const CxxDeclExplicitSafetyDescriptor &lhs,
const CxxDeclExplicitSafetyDescriptor &rhs) {
return lhs.decl == rhs.decl && lhs.isClass == rhs.isClass;
}
friend bool operator!=(const CxxDeclExplicitSafetyDescriptor &lhs,
const CxxDeclExplicitSafetyDescriptor &rhs) {
return !(lhs == rhs);
}
};
void simple_display(llvm::raw_ostream &out,
CxxDeclExplicitSafetyDescriptor desc);
SourceLoc extractNearestSourceLoc(CxxDeclExplicitSafetyDescriptor desc);
/// Determine the safety of the given Clang declaration.
class ClangDeclExplicitSafety
: public SimpleRequest<ClangDeclExplicitSafety,
ExplicitSafety(CxxDeclExplicitSafetyDescriptor),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;
// Source location
SourceLoc getNearestLoc() const { return SourceLoc(); };
bool isCached() const;
private:
friend SimpleRequest;
// Evaluation.
ExplicitSafety evaluate(Evaluator &evaluator,
CxxDeclExplicitSafetyDescriptor desc) const;
};
#define SWIFT_TYPEID_ZONE ClangImporter
#define SWIFT_TYPEID_HEADER "swift/ClangImporter/ClangImporterTypeIDZone.def"
#include "swift/Basic/DefineTypeIDZone.h"
#undef SWIFT_TYPEID_ZONE
#undef SWIFT_TYPEID_HEADER
// Set up reporting of evaluated requests.
template<typename Request>
void reportEvaluatedRequest(UnifiedStatsReporter &stats,
const Request &request);
#define SWIFT_REQUEST(Zone, RequestType, Sig, Caching, LocOptions) \
template <> \
inline void reportEvaluatedRequest(UnifiedStatsReporter &stats, \
const RequestType &request) { \
++stats.getFrontendCounters().RequestType; \
}
#include "swift/ClangImporter/ClangImporterTypeIDZone.def"
#undef SWIFT_REQUEST
} // end namespace swift
#endif // SWIFT_CLANG_IMPORTER_REQUESTS_H