brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.6 KiB · 03d2556 Raw
540 lines · c
1//===-- ClangASTImporter.h --------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGASTIMPORTER_H10#define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGASTIMPORTER_H11 12#include <map>13#include <memory>14#include <set>15#include <vector>16 17#include "clang/AST/ASTContext.h"18#include "clang/AST/ASTImporter.h"19#include "clang/AST/CharUnits.h"20#include "clang/AST/Decl.h"21#include "clang/AST/DeclCXX.h"22#include "clang/Basic/FileManager.h"23#include "clang/Basic/FileSystemOptions.h"24 25#include "lldb/Host/FileSystem.h"26#include "lldb/Symbol/CompilerDeclContext.h"27#include "lldb/Utility/LLDBAssert.h"28#include "lldb/lldb-types.h"29 30#include "Plugins/ExpressionParser/Clang/CxxModuleHandler.h"31 32#include "llvm/ADT/DenseMap.h"33 34namespace lldb_private {35 36class ClangASTMetadata;37class TypeSystemClang;38 39/// Manages and observes all Clang AST node importing in LLDB.40///41/// The ClangASTImporter takes care of two things:42///43/// 1. Keeps track of all ASTImporter instances in LLDB.44///45/// Clang's ASTImporter takes care of importing types from one ASTContext to46/// another. This class expands this concept by allowing copying from several47/// ASTContext instances to several other ASTContext instances. Instead of48/// constructing a new ASTImporter manually to copy over a type/decl, this class49/// can be asked to do this. It will construct a ASTImporter for the caller (and50/// will cache the ASTImporter instance for later use) and then perform the51/// import.52///53/// This mainly prevents that a caller might construct several ASTImporter54/// instances for the same source/target ASTContext combination. As the55/// ASTImporter has an internal state that keeps track of already imported56/// declarations and so on, using only one ASTImporter instance is more57/// efficient and less error-prone than using multiple.58///59/// 2. Keeps track of from where declarations were imported (origin-tracking).60/// The ASTImporter instances in this class usually only performa a minimal61/// import, i.e., only a shallow copy is made that is filled out on demand62/// when more information is requested later on. This requires record-keeping63/// of where any shallow clone originally came from so that the right original64/// declaration can be found and used as the source of any missing information.65class ClangASTImporter {66public:67  struct LayoutInfo {68    LayoutInfo() = default;69    typedef llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>70        OffsetMap;71 72    uint64_t bit_size = 0;73    uint64_t alignment = 0;74    llvm::DenseMap<const clang::FieldDecl *, uint64_t> field_offsets;75    OffsetMap base_offsets;76    OffsetMap vbase_offsets;77  };78 79  ClangASTImporter()80      : m_file_manager(clang::FileSystemOptions(),81                       FileSystem::Instance().GetVirtualFileSystem()) {}82 83  /// Copies the given type and the respective declarations to the destination84  /// type system.85  ///86  /// This function does a shallow copy and requires that the target AST87  /// has an ExternalASTSource which queries this ClangASTImporter instance88  /// for any additional information that is maybe lacking in the shallow copy.89  /// This also means that the type system of src_type can *not* be deleted90  /// after this function has been called. If you need to delete the source91  /// type system you either need to delete the destination type system first92  /// or use \ref ClangASTImporter::DeportType.93  ///94  /// \see ClangASTImporter::DeportType95  CompilerType CopyType(TypeSystemClang &dst, const CompilerType &src_type);96 97  /// \see ClangASTImporter::CopyType98  clang::Decl *CopyDecl(clang::ASTContext *dst_ctx, clang::Decl *decl);99 100  /// Copies the given type and the respective declarations to the destination101  /// type system.102  ///103  /// Unlike CopyType this function ensures that types/declarations which are104  /// originally from the AST of src_type are fully copied over. The type105  /// system of src_type can safely be deleted after calling this function.106  /// \see ClangASTImporter::CopyType107  CompilerType DeportType(TypeSystemClang &dst, const CompilerType &src_type);108 109  /// Copies the given decl to the destination type system.110  /// \see ClangASTImporter::DeportType111  clang::Decl *DeportDecl(clang::ASTContext *dst_ctx, clang::Decl *decl);112 113  /// Sets the layout for the given RecordDecl. The layout will later be114  /// used by Clang's during code generation. Not calling this function for115  /// a RecordDecl will cause that Clang's codegen tries to layout the116  /// record by itself.117  ///118  /// \param decl The RecordDecl to set the layout for.119  /// \param layout The layout for the record.120  void SetRecordLayout(clang::RecordDecl *decl, const LayoutInfo &layout);121 122  bool LayoutRecordType(123      const clang::RecordDecl *record_decl, uint64_t &bit_size,124      uint64_t &alignment,125      llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets,126      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>127          &base_offsets,128      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>129          &vbase_offsets);130 131  /// If \ref record has a valid origin, this function copies that132  /// origin's layout into this ClangASTImporter instance.133  ///134  /// \param[in] record The decl whose layout we're calculating.135  /// \param[out] size Size of \ref record in bytes.136  /// \param[out] alignment Alignment of \ref record in bytes.137  /// \param[out] field_offsets Offsets of fields of \ref record.138  /// \param[out] base_offsets Offsets of base classes of \ref record.139  /// \param[out] vbase_offsets Offsets of virtual base classes of \ref record.140  ///141  /// \returns Returns 'false' if no valid origin was found for \ref record or142  /// this function failed to import the layout from the origin. Otherwise,143  /// returns 'true' and the offsets/size/alignment are valid for use.144  bool importRecordLayoutFromOrigin(145      const clang::RecordDecl *record, uint64_t &size, uint64_t &alignment,146      llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets,147      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>148          &base_offsets,149      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>150          &vbase_offsets);151 152  /// Returns true iff the given type was copied from another TypeSystemClang153  /// and the original type in this other TypeSystemClang might contain154  /// additional information (e.g., the definition of a 'class' type) that could155  /// be imported.156  ///157  /// \see ClangASTImporter::Import158  bool CanImport(const CompilerType &type);159 160  bool CanImport(const clang::Decl *d);161 162  /// If the given type was copied from another TypeSystemClang then copy over163  /// all missing information (e.g., the definition of a 'class' type).164  ///165  /// \return True iff an original type in another TypeSystemClang was found.166  ///         Note: Does *not* return false if an original type was found but167  ///               no information was imported over.168  ///169  /// \see ClangASTImporter::Import170  bool Import(const CompilerType &type);171 172  bool CompleteType(const CompilerType &compiler_type);173 174  bool CompleteTagDecl(clang::TagDecl *decl);175 176  bool CompleteTagDeclWithOrigin(clang::TagDecl *decl, clang::TagDecl *origin);177 178  bool CompleteObjCInterfaceDecl(clang::ObjCInterfaceDecl *interface_decl);179 180  bool CompleteAndFetchChildren(clang::QualType type);181 182  bool RequireCompleteType(clang::QualType type);183 184  /// Updates the internal origin-tracking information so that the given185  /// 'original' decl is from now on used to import additional information186  /// into the given decl.187  ///188  /// Usually the origin-tracking in the ClangASTImporter is automatically189  /// updated when a declaration is imported, so the only valid reason to ever190  /// call this is if there is a 'better' original decl and the target decl191  /// is only a shallow clone that lacks any contents.192  void SetDeclOrigin(const clang::Decl *decl, clang::Decl *original_decl);193 194  std::optional<ClangASTMetadata> GetDeclMetadata(const clang::Decl *decl);195 196  //197  // Namespace maps198  //199 200  typedef std::pair<lldb::ModuleSP, CompilerDeclContext> NamespaceMapItem;201  typedef std::vector<NamespaceMapItem> NamespaceMap;202  typedef std::shared_ptr<NamespaceMap> NamespaceMapSP;203 204  void RegisterNamespaceMap(const clang::NamespaceDecl *decl,205                            NamespaceMapSP &namespace_map);206 207  NamespaceMapSP GetNamespaceMap(const clang::NamespaceDecl *decl);208 209  void BuildNamespaceMap(const clang::NamespaceDecl *decl);210 211  //212  // Completers for maps213  //214 215  class MapCompleter {216  public:217    virtual ~MapCompleter();218 219    virtual void CompleteNamespaceMap(NamespaceMapSP &namespace_map,220                                      ConstString name,221                                      NamespaceMapSP &parent_map) const = 0;222  };223 224  void InstallMapCompleter(clang::ASTContext *dst_ctx,225                           MapCompleter &completer) {226    ASTContextMetadataSP context_md;227    ContextMetadataMap::iterator context_md_iter = m_metadata_map.find(dst_ctx);228 229    if (context_md_iter == m_metadata_map.end()) {230      context_md = std::make_shared<ASTContextMetadata>(dst_ctx);231      m_metadata_map[dst_ctx] = context_md;232    } else {233      context_md = context_md_iter->second;234    }235 236    context_md->m_map_completer = &completer;237  }238 239  void ForgetDestination(clang::ASTContext *dst_ctx);240  void ForgetSource(clang::ASTContext *dst_ctx, clang::ASTContext *src_ctx);241 242  struct DeclOrigin {243    DeclOrigin() = default;244 245    DeclOrigin(clang::ASTContext *_ctx, clang::Decl *_decl)246        : ctx(_ctx), decl(_decl) {247      // The decl has to be in its associated ASTContext.248      assert(_decl == nullptr || &_decl->getASTContext() == _ctx);249    }250 251    DeclOrigin(const DeclOrigin &rhs) {252      ctx = rhs.ctx;253      decl = rhs.decl;254    }255 256    void operator=(const DeclOrigin &rhs) {257      ctx = rhs.ctx;258      decl = rhs.decl;259    }260 261    bool Valid() const { return (ctx != nullptr || decl != nullptr); }262 263    clang::ASTContext *ctx = nullptr;264    clang::Decl *decl = nullptr;265  };266 267  /// Listener interface used by the ASTImporterDelegate to inform other code268  /// about decls that have been imported the first time.269  struct NewDeclListener {270    virtual ~NewDeclListener() = default;271    /// A decl has been imported for the first time.272    virtual void NewDeclImported(clang::Decl *from, clang::Decl *to) = 0;273  };274 275  /// ASTImporter that intercepts and records the import process of the276  /// underlying ASTImporter.277  ///278  /// This class updates the map from declarations to their original279  /// declarations and can record declarations that have been imported in a280  /// certain interval.281  ///282  /// When intercepting a declaration import, the ASTImporterDelegate uses the283  /// CxxModuleHandler to replace any missing or malformed declarations with284  /// their counterpart from a C++ module.285  struct ASTImporterDelegate : public clang::ASTImporter {286    ASTImporterDelegate(ClangASTImporter &main, clang::ASTContext *target_ctx,287                        clang::ASTContext *source_ctx)288        : clang::ASTImporter(*target_ctx, main.m_file_manager, *source_ctx,289                             main.m_file_manager, true /*minimal*/),290          m_main(main), m_source_ctx(source_ctx) {291      // Target and source ASTContext shouldn't be identical. Importing AST292      // nodes within the same AST doesn't make any sense as the whole idea293      // is to import them to a different AST.294      lldbassert(target_ctx != source_ctx && "Can't import into itself");295      // This is always doing a minimal import of any declarations. This means296      // that there has to be an ExternalASTSource in the target ASTContext297      // (that should implement the callbacks that complete any declarations298      // on demand). Without an ExternalASTSource, this ASTImporter will just299      // do a minimal import and the imported declarations won't be completed.300      assert(target_ctx->getExternalSource() && "Missing ExternalSource");301      setODRHandling(clang::ASTImporter::ODRHandlingType::Liberal);302    }303 304    /// Scope guard that attaches a CxxModuleHandler to an ASTImporterDelegate305    /// and deattaches it at the end of the scope. Supports being used multiple306    /// times on the same ASTImporterDelegate instance in nested scopes.307    class CxxModuleScope {308      /// The handler we attach to the ASTImporterDelegate.309      CxxModuleHandler m_handler;310      /// The ASTImporterDelegate we are supposed to attach the handler to.311      ASTImporterDelegate &m_delegate;312      /// True iff we attached the handler to the ASTImporterDelegate.313      bool m_valid = false;314 315    public:316      CxxModuleScope(ASTImporterDelegate &delegate, clang::ASTContext *dst_ctx)317          : m_delegate(delegate) {318        // If the delegate doesn't have a CxxModuleHandler yet, create one319        // and attach it.320        if (!delegate.m_std_handler) {321          m_handler = CxxModuleHandler(delegate, dst_ctx);322          m_valid = true;323          delegate.m_std_handler = &m_handler;324        }325      }326      ~CxxModuleScope() {327        if (m_valid) {328          // Make sure no one messed with the handler we placed.329          assert(m_delegate.m_std_handler == &m_handler);330          m_delegate.m_std_handler = nullptr;331        }332      }333    };334 335    void ImportDefinitionTo(clang::Decl *to, clang::Decl *from);336 337    void Imported(clang::Decl *from, clang::Decl *to) override;338 339    clang::Decl *GetOriginalDecl(clang::Decl *To) override;340 341    void SetImportListener(NewDeclListener *listener) {342      assert(m_new_decl_listener == nullptr && "Already attached a listener?");343      m_new_decl_listener = listener;344    }345    void RemoveImportListener() { m_new_decl_listener = nullptr; }346 347  protected:348    llvm::Expected<clang::Decl *> ImportImpl(clang::Decl *From) override;349 350  private:351    void MarkDeclImported(clang::Decl *from, clang::Decl *to);352 353    /// Decls we should ignore when mapping decls back to their original354    /// ASTContext. Used by the CxxModuleHandler to mark declarations that355    /// were created from the 'std' C++ module to prevent that the Importer356    /// tries to sync them with the broken equivalent in the debug info AST.357    llvm::SmallPtrSet<clang::Decl *, 16> m_decls_to_ignore;358    ClangASTImporter &m_main;359    clang::ASTContext *m_source_ctx;360    CxxModuleHandler *m_std_handler = nullptr;361    /// The currently attached listener.362    NewDeclListener *m_new_decl_listener = nullptr;363  };364 365  typedef std::shared_ptr<ASTImporterDelegate> ImporterDelegateSP;366  typedef llvm::DenseMap<clang::ASTContext *, ImporterDelegateSP> DelegateMap;367  typedef llvm::DenseMap<const clang::NamespaceDecl *, NamespaceMapSP>368      NamespaceMetaMap;369 370  class ASTContextMetadata {371    typedef llvm::DenseMap<const clang::Decl *, DeclOrigin> OriginMap;372 373  public:374    ASTContextMetadata(clang::ASTContext *dst_ctx) : m_dst_ctx(dst_ctx) {}375 376    clang::ASTContext *m_dst_ctx;377    DelegateMap m_delegates;378 379    NamespaceMetaMap m_namespace_maps;380    MapCompleter *m_map_completer = nullptr;381 382    /// Sets the DeclOrigin for the given Decl and overwrites any existing383    /// DeclOrigin.384    void setOrigin(const clang::Decl *decl, DeclOrigin origin) {385      // Setting the origin of any decl to itself (or to a different decl386      // in the same ASTContext) doesn't make any sense. It will also cause387      // ASTImporterDelegate::ImportImpl to infinite recurse when trying to find388      // the 'original' Decl when importing code.389      assert(&decl->getASTContext() != origin.ctx &&390             "Trying to set decl origin to its own ASTContext?");391      assert(decl != origin.decl && "Trying to set decl origin to itself?");392      m_origins[decl] = origin;393    }394 395    /// Removes any tracked DeclOrigin for the given decl.396    void removeOrigin(const clang::Decl *decl) { m_origins.erase(decl); }397 398    /// Remove all DeclOrigin entries that point to the given ASTContext.399    /// Useful when an ASTContext is about to be deleted and all the dangling400    /// pointers to it need to be removed.401    void removeOriginsWithContext(clang::ASTContext *ctx) {402      for (OriginMap::iterator iter = m_origins.begin();403           iter != m_origins.end();) {404        if (iter->second.ctx == ctx)405          m_origins.erase(iter++);406        else407          ++iter;408      }409    }410 411    /// Returns the DeclOrigin for the given Decl or an invalid DeclOrigin412    /// instance if there no known DeclOrigin for the given Decl.413    DeclOrigin getOrigin(const clang::Decl *decl) const {414      auto iter = m_origins.find(decl);415      if (iter == m_origins.end())416        return DeclOrigin();417      return iter->second;418    }419 420    /// Returns true there is a known DeclOrigin for the given Decl.421    bool hasOrigin(const clang::Decl *decl) const {422      return getOrigin(decl).Valid();423    }424 425  private:426    /// Maps declarations to the ASTContext/Decl from which they were imported427    /// from. If a declaration is from an ASTContext which has been deleted428    /// since the declaration was imported or the declaration wasn't created by429    /// the ASTImporter, then it doesn't have a DeclOrigin and will not be430    /// tracked here.431    OriginMap m_origins;432  };433 434  typedef std::shared_ptr<ASTContextMetadata> ASTContextMetadataSP;435  typedef llvm::DenseMap<const clang::ASTContext *, ASTContextMetadataSP>436      ContextMetadataMap;437 438  ContextMetadataMap m_metadata_map;439 440  ASTContextMetadataSP GetContextMetadata(clang::ASTContext *dst_ctx) {441    ContextMetadataMap::iterator context_md_iter = m_metadata_map.find(dst_ctx);442 443    if (context_md_iter == m_metadata_map.end()) {444      ASTContextMetadataSP context_md =445          std::make_shared<ASTContextMetadata>(dst_ctx);446      m_metadata_map[dst_ctx] = context_md;447      return context_md;448    }449    return context_md_iter->second;450  }451 452  ASTContextMetadataSP MaybeGetContextMetadata(clang::ASTContext *dst_ctx) {453    ContextMetadataMap::iterator context_md_iter = m_metadata_map.find(dst_ctx);454 455    if (context_md_iter != m_metadata_map.end())456      return context_md_iter->second;457    return ASTContextMetadataSP();458  }459 460  ImporterDelegateSP GetDelegate(clang::ASTContext *dst_ctx,461                                 clang::ASTContext *src_ctx) {462    ASTContextMetadataSP context_md = GetContextMetadata(dst_ctx);463 464    DelegateMap &delegates = context_md->m_delegates;465    DelegateMap::iterator delegate_iter = delegates.find(src_ctx);466 467    if (delegate_iter == delegates.end()) {468      ImporterDelegateSP delegate =469          std::make_shared<ASTImporterDelegate>(*this, dst_ctx, src_ctx);470      delegates[src_ctx] = delegate;471      return delegate;472    }473    return delegate_iter->second;474  }475 476  DeclOrigin GetDeclOrigin(const clang::Decl *decl);477 478  clang::FileManager m_file_manager;479  typedef llvm::DenseMap<const clang::RecordDecl *, LayoutInfo>480      RecordDeclToLayoutMap;481 482  RecordDeclToLayoutMap m_record_decl_to_layout_map;483};484 485template <class D> class TaggedASTDecl {486public:487  TaggedASTDecl() : decl(nullptr) {}488  TaggedASTDecl(D *_decl) : decl(_decl) {}489  bool IsValid() const { return (decl != nullptr); }490  bool IsInvalid() const { return !IsValid(); }491  D *operator->() const { return decl; }492  D *decl;493};494 495template <class D2, template <class D> class TD, class D1>496TD<D2> DynCast(TD<D1> source) {497  return TD<D2>(llvm::dyn_cast<D2>(source.decl));498}499 500template <class D = clang::Decl> class DeclFromParser;501template <class D = clang::Decl> class DeclFromUser;502 503template <class D> class DeclFromParser : public TaggedASTDecl<D> {504public:505  DeclFromParser() : TaggedASTDecl<D>() {}506  DeclFromParser(D *_decl) : TaggedASTDecl<D>(_decl) {}507 508  DeclFromUser<D> GetOrigin(ClangASTImporter &importer);509};510 511template <class D> class DeclFromUser : public TaggedASTDecl<D> {512public:513  DeclFromUser() : TaggedASTDecl<D>() {}514  DeclFromUser(D *_decl) : TaggedASTDecl<D>(_decl) {}515 516  DeclFromParser<D> Import(clang::ASTContext *dest_ctx,517                           ClangASTImporter &importer);518};519 520template <class D>521DeclFromUser<D> DeclFromParser<D>::GetOrigin(ClangASTImporter &importer) {522  ClangASTImporter::DeclOrigin origin = importer.GetDeclOrigin(this->decl);523  if (!origin.Valid())524    return DeclFromUser<D>();525  return DeclFromUser<D>(llvm::dyn_cast<D>(origin.decl));526}527 528template <class D>529DeclFromParser<D> DeclFromUser<D>::Import(clang::ASTContext *dest_ctx,530                                          ClangASTImporter &importer) {531  DeclFromParser<> parser_generic_decl(importer.CopyDecl(dest_ctx, this->decl));532  if (parser_generic_decl.IsInvalid())533    return DeclFromParser<D>();534  return DeclFromParser<D>(llvm::dyn_cast<D>(parser_generic_decl.decl));535}536 537} // namespace lldb_private538 539#endif // LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGASTIMPORTER_H540