Skip to content
Snippets Groups Projects
Clang_utils.h 36.92 KiB
/**************************************************************************/
/*                                                                        */
/*  This file is part of Frama-Clang                                      */
/*                                                                        */
/*  Copyright (C) 2012-2020                                               */
/*    CEA (Commissariat à l'énergie atomique et aux énergies              */
/*         alternatives)                                                  */
/*                                                                        */
/*  you can redistribute it and/or modify it under the terms of the GNU   */
/*  Lesser General Public License as published by the Free Software       */
/*  Foundation, version 2.1.                                              */
/*                                                                        */
/*  It is distributed in the hope that it will be useful,                 */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of        */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         */
/*  GNU Lesser General Public License for more details.                   */
/*                                                                        */
/*  See the GNU Lesser General Public License version 2.1                 */
/*  for more details (enclosed in the file LICENSE).                      */
/*                                                                        */
/**************************************************************************/

// -*- C++ -*-
/* Functions for manipulating Clang structures and
   transforming them into intermediate AST, that are shared between main AST
   and ACSL
 */

#ifndef Clang_utilsH
#define Clang_utilsH

#include <iostream>
#include <sstream>
#include <cstddef>
#include <list>
#include <set>
#include <map>
#include <functional>
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#include "clang/Basic/Version.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

extern "C" {
#include "intermediate_format.h"

void free_type(qual_type obj);

}

static inline const char*
copy_string(const std::string& s) {
   return (s.length() > 0) ? strdup(s.c_str()) : strdup("");
}

location copy_loc(location);
void free_location(location);

bool is_same_qualification(/* qualification */list, /*qualification*/list);
int compare_qualification(/* qualification */list, /*qualification*/list);

static inline void
extend_location_with(location source, location extension)
{ source->linenum2 = extension->linenum2;
  source->charnum2 = extension->charnum2;
}

static inline list cv_this_ptr(const clang::CXXMethodDecl* meth) {
  list /*specifier*/ qual = NULL;
  if (meth->isConst()) qual = cons_plain(CONST,qual);
  if (meth->isVolatile()) qual = cons_plain(VOLATILE,qual);
  return qual;
  }

static inline bool
isTemplateInstance(clang::TemplateSpecializationKind kind) {
  return clang::isTemplateInstantiation(kind);
}

namespace clang {
class Sema;
}

namespace Acsl {

class GlobalContext {
private:
  int _variableNumber;

public:
  class LogicVariable;
  class OverloadedLogicFunctions;
  class OverloadedLogicOperators;
  class LogicType;
  class LogicConstructor;
  class Qualification;
  class TemplateQualification;

  class NestedContext {
  private:
    NestedContext* _parent;
    std::string _name;

  public:
    NestedContext(const std::string& name) : _parent(NULL), _name(name) {}
    NestedContext(const NestedContext& source)
      : _parent(NULL), _name(source._name) {}
    virtual ~NestedContext() {}

    const std::string& getName() const { return _name; }
    enum Type
      { TUndefined, TLogicVariable, TOverloadedLogicFunctions,
        TLogicType, TLogicConstructor, TQualification, TTemplateQualification
      };
    virtual Type getType() const { return TUndefined; }
    bool isLogicVariable() const { return getType() == TLogicVariable; }
    bool isOverloadedLogicFunctions() const
      { return getType() == TOverloadedLogicFunctions; }
    bool isLogicType() const { return getType() == TLogicType; }
    bool isLogicConstructor() const { return getType() == TLogicConstructor; }
    bool isQualification() const { return getType() == TQualification; }
    bool isTemplateQualification() const
      { return getType() == TTemplateQualification; }

    LogicVariable& asLogicVariable();
    const LogicVariable& asLogicVariable() const;
    OverloadedLogicFunctions& asOverloadedLogicFunctions();
    const OverloadedLogicFunctions& asOverloadedLogicFunctions() const;
    LogicType& asLogicType();
    const LogicType& asLogicType() const;
    LogicConstructor& asLogicConstructor();
    const LogicConstructor& asLogicConstructor() const;
    Qualification& asQualification();
    const Qualification& asQualification() const;
    TemplateQualification& asTemplateQualification();
    const TemplateQualification& asTemplateQualification() const;

    NestedContext* sparent() const { return _parent; }
    void setParent(NestedContext* parent)
      { assert(!_parent); _parent = parent; }
    bool hasParent() const { return _parent != NULL; }
    bool hasSons() const 
      { return const_cast<NestedContext*>(this)->ssons() != NULL; }
       
    struct Less {
      bool operator()(const NestedContext* c1, const NestedContext* c2) const
        { if(!c1) return c2; // NULL is less than everything
          if(!c2) return false;
          return c1->compare(*c2) < 0;
        }
    };
    virtual int compare(const NestedContext& c) const
      { return this->_name.compare(c._name); }
    typedef std::set<NestedContext*, Less> SonsSet;
    virtual SonsSet* ssons() { return NULL; }
    SonsSet& sons()
      { SonsSet* result = ssons();
        assert(result);
        return *result;
      }
    const SonsSet& sons() const
      { SonsSet* result = const_cast<NestedContext*>(this)->ssons();
        assert(result);
        return *result;
      }
  };

private:
  NestedContext::SonsSet _logicTable;

  void init(); // add Utf8_logic::boolean

public:
  GlobalContext() : _variableNumber(0) { init(); }
  ~GlobalContext()
    { NestedContext::SonsSet::iterator iterEnd = _logicTable.end();
      for (NestedContext::SonsSet::iterator iter = _logicTable.begin();
          iter != iterEnd; ++iter)
        delete *iter;
    }

  int& variableNumber() { return _variableNumber; }
  const NestedContext::SonsSet& logicTable() const { return _logicTable; }
  NestedContext::SonsSet& logicTable() { return _logicTable; }
  NestedContext* find(const std::string& identifier, NestedContext* start)
      const;
  NestedContext* find(qualified_name identifier, NestedContext* start) const;
  NestedContext* findAbsolute(qualified_name identifier) const;
};
   
class GlobalContext::LogicVariable : public GlobalContext::NestedContext {
private:
  typedef NestedContext inherited;
  logic_var lvVariable;

public:
  LogicVariable(const std::string& name, logic_var variable)
    : inherited(name), lvVariable(variable) {}
  virtual ~LogicVariable() { free_logic_var(lvVariable); }
  virtual Type getType() const { return TLogicVariable; }
};

class GlobalContext::OverloadedLogicFunctions
    : public GlobalContext::NestedContext {
public:
  typedef std::list<std::pair<bool, logic_info> > Functions;

private:
  typedef NestedContext inherited;
  Functions _logicFunctions;
    // the first bool is the method flag

public:
  OverloadedLogicFunctions(const std::string& name, logic_info info,
      bool isMethod=false)
    : inherited(name)
    { _logicFunctions.push_back(std::make_pair(isMethod, info)); }
  virtual ~OverloadedLogicFunctions()
    { Functions::iterator iterEnd = _logicFunctions.end();
      for (Functions::iterator iter = _logicFunctions.begin();
          iter != iterEnd; ++iter)
        free_logic_info(iter->second);
    }
  virtual Type getType() const { return TOverloadedLogicFunctions; }
  virtual bool isOperator() const { return false; }
  OverloadedLogicOperators& asOperator();
  void addFunction(logic_info info, bool isMethod=false)
    { _logicFunctions.push_back(std::make_pair(isMethod, info)); }
  const Functions& getFunctions() const { return _logicFunctions; }
};

class GlobalContext::OverloadedLogicOperators
    : public GlobalContext::OverloadedLogicFunctions {
private:
  typedef GlobalContext::OverloadedLogicFunctions inherited;
  int _codeOperator;

public:
  OverloadedLogicOperators(const std::string& name, int codeOperator,
      logic_info info, bool isMethod=false)
    : inherited(name, info, isMethod), _codeOperator(codeOperator) {}
  virtual bool isOperator() const { return true; }
  int getCodeOperator() const { return _codeOperator; }
};

inline GlobalContext::OverloadedLogicOperators&
GlobalContext::OverloadedLogicFunctions::asOperator()
  { return (GlobalContext::OverloadedLogicOperators&) *this; }

class GlobalContext::LogicType : public GlobalContext::NestedContext {
private:
  typedef NestedContext inherited;
  logic_type_info _type;

public:
  LogicType(const std::string& name, logic_type_info type)
    : inherited(name), _type(type) {}
  virtual ~LogicType() { free_logic_type_info(_type); }
  virtual Type getType() const { return TLogicType; }
  logic_type_info type_info() const { return _type; }
};

class GlobalContext::LogicConstructor : public GlobalContext::NestedContext {
private:
  typedef NestedContext inherited;
  logic_ctor_info lciConstructor;

public:
  LogicConstructor(const std::string& name, logic_ctor_info constructor)
    : inherited(name), lciConstructor(constructor) {}
  virtual ~LogicConstructor() { free_logic_ctor_info(lciConstructor); }
  virtual Type getType() const { return TLogicConstructor; }
  logic_ctor_info getInfo() const { return lciConstructor; }
};

class GlobalContext::Qualification : public GlobalContext::NestedContext {
private:
  typedef NestedContext inherited;
  NestedContext::SonsSet mSons;
  tag_qualification tag;
  // + usings.

public:
  Qualification(const std::string& name, tag_qualification t)
    : inherited(name), tag(t) {}
  virtual ~Qualification()
    { NestedContext::SonsSet::iterator iterEnd = mSons.end();
      for (NestedContext::SonsSet::iterator iter = mSons.begin();
          iter != iterEnd; ++iter)
        delete *iter;
    }
  virtual Type getType() const { return TQualification; }
  virtual NestedContext::SonsSet* ssons() { return &mSons; }
  bool hasRecordType() const { return tag == QSTRUCTORCLASS; }
  bool hasTemplateRecordType() const { return tag == QTEMPLATEINSTANCE; }

  TemplateQualification* findInstance(/* template_parameter */ list parameters)
      const;
  qualified_name makeRecordName() const;
  /* qualification */ list makeQualificationList() const;
  qualification getQualification() const {
    const char* name = strdup(getName().c_str());
    switch(tag) {
      case QNAMESPACE:
        return qualification_QNamespace(name);
      case QSTRUCTORCLASS:
        return qualification_QStructOrClass(name);
      case QTEMPLATEINSTANCE:
        assert(false);
    }
    assert(false);
    return NULL;
  }
};

class GlobalContext::TemplateQualification
    : public GlobalContext::NestedContext {
private:
  typedef NestedContext inherited;
  NestedContext::SonsSet mSons;
  /* template_parameter */ list _parameters;
  friend class Qualification;
  friend class GlobalContext;

public:
  TemplateQualification(/* template_parameter */ list parameters)
    : inherited(""), _parameters(NULL)
    { /* template_parameter */ list* endParameters = &_parameters;
      while (parameters) {
        *endParameters = cons_container(template_parameter_dup(
          (template_parameter) parameters->element.container), NULL);
        endParameters = &((*endParameters)->next);
        parameters = parameters->next;
      };
    }
  virtual ~TemplateQualification()
    { while (_parameters) {
        free_template_parameter((template_parameter) _parameters
            ->element.container);
        /* template_parameter */ list temp = _parameters;
        _parameters = _parameters->next;
        free(temp);
      };
      NestedContext::SonsSet::iterator iterEnd = mSons.end();
      for (NestedContext::SonsSet::iterator iter = mSons.begin();
          iter != iterEnd; ++iter)
        delete *iter;
    }
  /* qualification */ list makeQualificationList() const;
  qualification getQualification(const char* name) const
    { /* template_parameter */ list parameters = _parameters;
      /* template_parameter */ list result = NULL;
      /* template_parameter */ list* endResult = &result;
      while (parameters) {
        *endResult = cons_container(template_parameter_dup((template_parameter)
            parameters->element.container), NULL);
        endResult = &(*endResult)->next;
        parameters = parameters->next;
      };
      return qualification_QTemplateInstance(name, result);
    }
  virtual int compare(const NestedContext& c) const;
  virtual Type getType() const { return TTemplateQualification; }
  virtual NestedContext::SonsSet* ssons() { return &mSons; }
  /* template_parameter */ list getParameters() const { return _parameters; }
  /* template_parameter */ list extractParameters()
    { list result = _parameters; _parameters = NULL; return result; }
};

inline GlobalContext::LogicVariable&
GlobalContext::NestedContext::asLogicVariable()
  { assert(getType() == TLogicVariable); return (LogicVariable&) *this; }
  
inline const GlobalContext::LogicVariable&
GlobalContext::NestedContext::asLogicVariable() const
  { assert(getType() == TLogicVariable); return (const LogicVariable&) *this; }
  
inline GlobalContext::OverloadedLogicFunctions&
GlobalContext::NestedContext::asOverloadedLogicFunctions()
  { assert(getType() == TOverloadedLogicFunctions);
    return (OverloadedLogicFunctions&) *this;
  }
  
inline const GlobalContext::OverloadedLogicFunctions&
GlobalContext::NestedContext::asOverloadedLogicFunctions() const
  { assert(getType() == TOverloadedLogicFunctions);
    return (const OverloadedLogicFunctions&) *this;
  }
  
inline GlobalContext::LogicType&
GlobalContext::NestedContext::asLogicType()
  { assert(getType() == TLogicType); return (LogicType&) *this; }
  
inline const GlobalContext::LogicType&
GlobalContext::NestedContext::asLogicType() const
  { assert(getType() == TLogicType); return (const LogicType&) *this; }
  
inline GlobalContext::LogicConstructor&
GlobalContext::NestedContext::asLogicConstructor()
  { assert(getType() == TLogicConstructor); return (LogicConstructor&) *this; }
  
inline const GlobalContext::LogicConstructor&
GlobalContext::NestedContext::asLogicConstructor() const
{ assert(getType() == TLogicConstructor);
  return (const LogicConstructor&) *this;
}
  
inline GlobalContext::Qualification&
GlobalContext::NestedContext::asQualification()
  { assert(getType() == TQualification); return (Qualification&) *this; }
  
inline const GlobalContext::Qualification&
GlobalContext::NestedContext::asQualification() const
  { assert(getType() == TQualification); return (const Qualification&) *this; }

inline GlobalContext::TemplateQualification&
GlobalContext::NestedContext::asTemplateQualification()
{ assert(getType() == TTemplateQualification);
  return (TemplateQualification&) *this;
}
  
inline const GlobalContext::TemplateQualification&
GlobalContext::NestedContext::asTemplateQualification() const
{ assert(getType() == TTemplateQualification);
  return (const TemplateQualification&) *this;
}

} // end of namespace Acsl

// Some smart constructors for intermediate AST

namespace Intermediate_ast {
  // the functions below duplicate the location and the string given as
  // argument, but not the AST nodes themselves.
  // Hence, caller has to ensure that such nodes are fresh.


  expression makeFloatConstant(const location loc, fkind k, const char* repr);

  expression makeIntLiteral(const location loc, ikind k, long value);

  expression makeStringLiteral(const location loc, const char* str);
 
  expression makeFloatZero(const location loc, fkind k);

  /* null pointer for an object type. */
  expression makeNullPointer(const location loc, qual_type pointee);

  /* unqualified and untemplated struct T type */
  qual_type makeStructType(qualified_name struct_name);
}

using namespace Intermediate_ast;

class FramacVisitor;

class RTTITable;
class ForwardReferenceList;
class Clang_utils {
public:
  class VirtualDeclRegistration {
  private:
    bool _doesRegisterDecl;

  protected:
    void setRegisterDecl() { _doesRegisterDecl = true; }
    void clearRegisterDecl() { _doesRegisterDecl = false; }

  public:
    VirtualDeclRegistration() : _doesRegisterDecl(false) {}
    VirtualDeclRegistration(const VirtualDeclRegistration& source)
      : _doesRegisterDecl(source._doesRegisterDecl) {}
    virtual ~VirtualDeclRegistration() {}

    bool doesRegisterDecl() const { return _doesRegisterDecl; }
    virtual void registerDecl(const clang::Decl* decl) {}
    virtual VirtualDeclRegistration* getNameRegistration() { return this; }
  };

private:
  mutable int _anonymousIdent;
  mutable std::map<const clang::Decl*, std::string> _anonymousMap;
  clang::ASTContext* _context;
  FramacVisitor* _caller;
  mutable Acsl::GlobalContext _acslContext;
  mutable std::vector<std::pair<const clang::Decl*, 
      const clang::TemplateArgumentList*> > _defaultTemplateInstances;

  bool _annotError;
  bool _doesGenerateImplicitMethods;
  bool _doesGenerateBareFunctions;
  bool _isVerbose;

  /// encapsulation of a method operating on plain (non-sugared) types
  template <typename T,
            T (Clang_utils::*builtinMethod) (clang::Type const*)const>
  class liftType {
    private:
    const Clang_utils* _clang_utils;
    public:
    T null;
    liftType(const Clang_utils* clang_utils, T dft):
      _clang_utils(clang_utils), null(dft) {}
    T operator()(const clang::Type* type) const {
        return (_clang_utils->*builtinMethod)(type);
    }
  };

  /// specialization of liftType for the case where the type is a
  /// C++ builtin-type
  template <typename T,
            T (Clang_utils::*builtinMethod) (clang::BuiltinType const*)const>
  class liftBuiltinType {
    private:
    const Clang_utils* _clang_utils;
    public:
    T null;
    liftBuiltinType(const Clang_utils* clang_utils, T dft):
      _clang_utils(clang_utils), null(dft) {}
    T operator()(const clang::Type* type) const {
      if (type->isBuiltinType())
        return
          (_clang_utils->*builtinMethod)(
            llvm::dyn_cast<clang::BuiltinType const>(type));
      return null;
    }
  };

  /// calls a given method on the given type after all possible desugaring
  /// (typedef, template instantiation, type inference, ... has been done.
  /// Op is supposed to be one of the two classes above
  template <typename T, class Op>
  T makeSpecificType(const Op* op, const clang::Type* type) const;

public:
  Clang_utils(clang::ASTContext*ctxt, FramacVisitor* caller)
    : _anonymousIdent(0), _anonymousMap(), _context(ctxt), _caller(caller),
      _annotError(false),_doesGenerateImplicitMethods(false),
      _doesGenerateBareFunctions(false),_isVerbose(false)
      { }

  location makeLocation(clang::SourceRange const& locs) const;
  // methods accessible for the debugger without any temp object on the stack
  location makeSingleLocation(clang::SourceLocation const& loc) const;
  location makeDeclLocation(clang::Decl const& decl) const;
  location makeExprLocation(clang::Expr const& expr) const;

  clang::SourceLocation getBeginLoc(clang::Stmt const& stmt) const {
#if CLANG_VERSION_MAJOR >= 8
    return stmt.getBeginLoc();
#else
    return stmt.getLocStart();
#endif
  }

  clang::SourceLocation getBeginLoc(clang::Decl const& d) const {
#if CLANG_VERSION_MAJOR >= 8
    return d.getBeginLoc();
#else
    return d.getLocStart();
#endif
  }

  clang::SourceLocation getBeginLoc(clang::CXXBaseSpecifier const& b) const {
#if CLANG_VERSION_MAJOR >= 8
    return b.getBeginLoc();
#else
    return b.getLocStart();
#endif
  }

  clang::SourceLocation getEndLoc(clang::Stmt const& stmt) const {
#if CLANG_VERSION_MAJOR >= 8
    return stmt.getEndLoc();
#else
    return stmt.getLocEnd();
#endif
  }

  clang::SourceLocation getEndLoc(clang::Decl const& d) const {
#if CLANG_VERSION_MAJOR >= 8
    return d.getEndLoc();
#else
    return d.getLocEnd();
#endif
  }

  void setAnnotError() { _annotError = true; }
  void setGenerateImplicitMethods() { _doesGenerateImplicitMethods = true; }
  void setGenerateBareFunctions() { _doesGenerateBareFunctions = true; }
  void setVerbose() { _isVerbose = true; }
  bool stopOnAnnotError () const { return _annotError; }
  bool doesGenerateImplicitMethods() const
    { return _doesGenerateImplicitMethods; }
  bool doesGenerateBareFunctions() const
    { return _doesGenerateBareFunctions; }
  bool isVerbose() const { return _isVerbose; }

  void pushTemplateInstance(const clang::Decl* templateDecl,
      const clang::TemplateArgumentList* instanceArguments)
    { _defaultTemplateInstances.push_back(
        std::make_pair(templateDecl, instanceArguments));
    }
  void popTemplateInstance(const clang::Decl* templateDecl)
    { assert(_defaultTemplateInstances.size() > 0
        && _defaultTemplateInstances.back().first == templateDecl);
      _defaultTemplateInstances.pop_back();
    }
  bool hasTemplateInstance() const
    { return _defaultTemplateInstances.size() > 0; }
  const clang::TemplateArgumentList* findInstanceArguments(
      const clang::Decl* templateDecl) const;
  const clang::TemplateArgument* findTemplateArgument(const std::string& name,
      const clang::NamedDecl*& templateParameter) const;
  Acsl::GlobalContext::NestedContext* queryDeclLogicScope(
      const clang::DeclContext* clangScope) const;

  void set_context(clang::ASTContext* ctxt) { _context = ctxt; }
  const clang::NamedDecl* findAnonymousDecl(const std::string& name) const;
  std::string findAnonymousName(const clang::NamedDecl* decl) const;

  // returns the fully qualified name of the declared identifier
  qualified_name makeQualifiedName(const clang::NamedDecl&,
      bool doesAcceptInstanceFail=false) const;
  signature makeSignature(const clang::FunctionDecl&) const;
  signature makeSignatureForMangling(const clang::FunctionDecl& function) const;

  // given a declaration context and a name, returns the corresponding
  // fully qualified name. name and decl cannot be NULL at the same time.
  qualified_name makeQualifiedName(const clang::DeclContext*Ctx,
      const char* name, const clang::NamedDecl* decl=NULL,
      tkind* templateParameters=NULL, bool doesAcceptInstanceFail=false) const;
  Acsl::GlobalContext& globalAcslContext() const { return _acslContext; }

  bool isIntegralType(clang::BuiltinType const* typ) const;
  bool isSignedType(clang::BuiltinType const* typ) const;
  bool isArithmeticType(clang::BuiltinType const* typ) const;
  bool isFloatingType(clang::BuiltinType const* typ) const;

  /// returns true if the type is directly a pointer.
  /// use isPointerType for desugaring the argument first.
  bool isPlainPointer(clang::Type const* typ) const;

  /// returns true if the type is directly a reference.
  /// use isReferenceType for desugaring the argument first
  bool isPlainReference(clang::Type const* typ) const;

  /// returns true if the type is directly an array.
  /// use isPlainArray for desugaring the argument first.
  bool isPlainArray(clang::Type const* typ) const;

  /// returns true if the expression is an lvalue with a reference type.
  /// we can't directly use expr->getType(), as the type of expressions
  /// is adjusted not to be a reference as per Clause 5[expr], §5
  bool lvalHasRefType(clang::Expr const* expr) const;

  bool is_lambda(clang::RecordDecl const* record) const;

  /*capture*/ list make_capture_list(
    clang::CXXRecordDecl::capture_const_range captures) const;

  typ make_lambda_type(
    clang::SourceLocation const& loc, clang::RecordDecl const* record,
    VirtualDeclRegistration* declRegistration=NULL) const;

  typ makeBuiltinType(
    clang::SourceLocation const& loc, clang::BuiltinType const* typ) const;
  typ makeBuiltinTypeNoLoc(clang::BuiltinType const* typ) const
    { return makeBuiltinType(clang::SourceLocation(),typ); }
 
  typ charType() const; /* plain char type (signedness depends on arch) */
  typ charPtrType() const; /* pointer to plain char. */

  qual_type charQualType() const { return qual_type_cons(NULL, charType()); }
  qual_type charPtrQualType() const {
    return qual_type_cons(NULL, charPtrType()); }

  logic_type makeBuiltinLogicType(
    clang::SourceLocation const& loc, clang::BuiltinType const* typ) const;
  typ makeArithmeticType(const clang::Type* type) const;

  ikind makePlainIntConstantType(const clang::Type* type) const;
  fkind makeFloatConstantType(const clang::BuiltinType* type) const;
  ikind makeIntConstantType(const clang::Type* type) const;
  fkind makeFloatConstantType(const clang::Type* type) const;

  typedef std::vector<const clang::Decl*> UnvisitedDecls;
  typ makePlainType(
    clang::SourceLocation const& loc,
    clang::QualType const& qt, 
    VirtualDeclRegistration* declRegistration=NULL, bool isPOD=false) const;
  qual_type makeType(
    clang::SourceLocation const& loc,
    clang::QualType const& qt,
    VirtualDeclRegistration* declRegistration=NULL) const;
  funkind cv_meth(const clang::CXXMethodDecl* meth) const
    { funkind result;
      if (meth->getKind() != clang::Decl::CXXConversion)
        result = funkind_FKMethod(cv_this_ptr(meth));
      else
        result =
          funkind_FKCastMethodOperator(
            cv_this_ptr(meth),
            makeType(
              meth->getLocation(),
              static_cast<const clang::CXXConversionDecl*>(meth)
                ->getConversionType()));
      return result;
    }
  qual_type makePODType(
    clang::SourceLocation const& loc, clang::QualType const& qt) const;
  logic_type makeLogicType(
    clang::SourceLocation const& loc, clang::Type const* type) const;
  logic_type makePointedType(
    clang::SourceLocation const& loc, clang::Type const* type) const;
  logic_type makeReferencedType(
    clang::SourceLocation const& loc, clang::Type const* type) const;
  logic_type makeElementArrayType(
    clang::SourceLocation const& loc, clang::Type const* type) const;
  qualified_name makeCompoundType(clang::Type const* type, tkind* templateKind)
      const;
  logic_type logicArithmeticPromotion(
    clang::SourceLocation const& loc, clang::Type const* type) const;
  bool isIntegralType(clang::Type const* type) const;
  bool isSignedType(clang::Type const* type) const;
  bool isArithmeticType(clang::Type const* type) const;
  bool isFloatingType(clang::Type const* type) const;
  bool isPointerType(clang::Type const* type) const;
  bool isReferenceType(clang::Type const* type) const;
  bool isArrayType(clang::Type const* type) const;

  bool isFunctionReferenceType(typ type) const;
  bool isObjectReferenceType(typ type) const;
  qual_type getObjectReferenceType(typ type) const;

  const clang::ConstantArrayType* isConstantArrayType(clang::Type const* type)
      const;
  bool retrieveTypeOfField(clang::Type const* type,
      const std::string& fieldName, term_offset& offset, logic_type& ltype,
      std::string& errorMessage, const clang::ASTContext* clangAST,
      clang::Sema* clangSema, const clang::SourceLocation& location,
      const RTTITable& rttiTable) const;
  template_parameter getTemplateExtension(
      const clang::SourceLocation & loc,
      const clang::TemplateArgument& parameter,
      /* template_parameter */ ForwardReferenceList& parametersList) const;
  /* template_parameter */ list getTemplateExtension(
      const clang::SourceLocation & loc,
      const clang::TemplateArgumentList& parameters) const;
  /* template_parameter */ list getTemplateExtension(
      const clang::ClassTemplateSpecializationDecl* inst) const
    { return
        getTemplateExtension(
          inst->getPointOfInstantiation(), inst->getTemplateArgs());
    }
   /* template_parameter */ list getTemplateExtension(
      const clang::FunctionTemplateSpecializationInfo* inst) const
    { return
        getTemplateExtension(
          inst->getPointOfInstantiation(), *inst->TemplateArguments);
    }
  const char* get_field_name(const clang::NamedDecl*) const;
  const char* get_aggregate_name(const clang::RecordDecl*,
      tkind* templateParameters) const;
  tkind makeTemplateKind(const clang::RecordDecl*) const;
  list /* specifier */ make_specifier_list(clang::QualType const& qt) const
    { list/*<specifier>*/ spec = NULL;
      if (qt.isLocalRestrictQualified())
        spec = cons_plain(RESTRICT,spec);
      if (qt.isLocalVolatileQualified())
        spec = cons_plain(VOLATILE,spec);
      if (qt.isLocalConstQualified())
        spec = cons_plain(CONST,spec);
      return spec;
    }

  int& logicVariableNumber() const { return _acslContext.variableNumber(); }

  static bool isExternCContext(const clang::DeclContext* ctx);

  std::string loc_as_string(const clang::SourceLocation& loc) const {
    return loc.printToString(_context->getSourceManager());
  }

  clang::SourceLocation getSourceLocation(const location loc) const {
    if (!loc) return clang::SourceLocation();
    const clang::SourceManager& sm = _context->getSourceManager();
#if CLANG_VERSION_MAJOR >= 10
    auto fileOpt = sm.getFileManager().getFileRef(std::string(loc->filename1));
    if (fileOpt) {
      const clang::FileEntry& file = fileOpt.get().getFileEntry();
      return sm.translateFileLineCol(&file, loc->linenum1, loc->charnum1);
    } else {
      // use dummy FileID if we don't have a valid FileEntry
      return sm.translateLineCol(clang::FileID(), loc->linenum1, loc->charnum1);
    }
#else
    const clang::FileEntry* file=sm.getFileManager().getFile(std::string(loc->filename1));
    return sm.translateFileLineCol(file,loc->linenum1,loc->charnum1);
#endif
  }

};

class ForwardList;
class ForwardReferenceList {
private:
  list* _front;
  list _back;
  friend class ForwardList;

public:
  ForwardReferenceList() : _front(NULL), _back(NULL) {}
  ForwardReferenceList(const ForwardReferenceList& source)
    : _front(source._front), _back(source._back) {}
  ForwardReferenceList(list& alist) : _front(&alist), _back(alist)
    { while (_back && _back->next) _back = _back->next; }

  bool isValid() const
    { return _front && (!*_front ? !_back : (_back && !_back->next)); }

  void resetBack(list back)
    { _back = back;
      while (_back && _back->next) _back = _back->next;
    }
  void clear()
    { if (_front) {
        _back = *_front;
        while (_back && _back->next)
          _back = _back->next;
      }
      else
        _back = NULL;
    }
  void advanceToEnd()
    { if (_front) {
        if (!_back)
          _back = *_front;
        while (_back && _back->next)
          _back = _back->next;
      }
      else
        _back = NULL;
    }
  ForwardReferenceList& insertPlain(long value)
    { assert(_front);
      if (_back) {
        assert(!_back->next);
        _back->next = cons_plain(value, NULL);
        _back = _back->next;
      }
      else
        *_front = _back = cons_plain(value, NULL);
      return *this;
    }
  ForwardReferenceList& insertContainer(void* value)
    { assert(_front);
      if (_back) {
        assert(!_back->next);
        _back->next = cons_container(value, NULL);
        _back = _back->next;
      }
      else
        *_front = _back = cons_container(value, NULL);
      return *this;
    }
  ForwardReferenceList& append(ForwardReferenceList& tail)
    { assert(_front && tail._front);
      if (_back) {
        if (tail._back) {
          _back->next = *tail._front;
          _back = tail._back;
        };
      }
      else {
        *_front = *tail._front;;
        _back = tail._back;
      };
      return *this;
    }
  ForwardReferenceList& append(ForwardList& tail);
  list getBack() const { return _back; }
  list& getFront() const { assert(_front); return *_front; }
};

class ForwardDoubleReferenceList : public ForwardReferenceList {
private:
  list* _beforeBack;
  friend class ForwardList;

public:
  ForwardDoubleReferenceList() : _beforeBack(NULL) {}
  ForwardDoubleReferenceList(const ForwardDoubleReferenceList& source)
    : ForwardReferenceList(source), _beforeBack(NULL) {}
  ForwardDoubleReferenceList(list& alist) : ForwardReferenceList(alist),
      _beforeBack(NULL) {}

  void clear()
    { ForwardReferenceList::clear();
      _beforeBack = NULL;
    }
  void setBeforeBack(list* beforeBack) { _beforeBack = beforeBack; }
  ForwardDoubleReferenceList& insertPlain(long value)
    { if (getBack())
        _beforeBack = &getBack()->next;
      return (ForwardDoubleReferenceList&)
        ForwardReferenceList::insertPlain(value);
    }
  ForwardDoubleReferenceList& insertContainer(void* value)
    { if (getBack())
        _beforeBack = &getBack()->next;
      return (ForwardDoubleReferenceList&)
        ForwardReferenceList::insertContainer(value);
    }
  ForwardDoubleReferenceList& insertBeforeContainer(void* value)
    { if (_beforeBack) {
        list next = *_beforeBack;
        *_beforeBack = cons_container(value, NULL);
        (*_beforeBack)->next = next;
        _beforeBack = &(*_beforeBack)->next;
      }
      else {
        _beforeBack = &getBack()->next;
        ForwardReferenceList::insertContainer(value);
      };
      return *this;
    }
  ForwardDoubleReferenceList& append(ForwardReferenceList& tail)
    { _beforeBack = NULL;
      return (ForwardDoubleReferenceList&)
        ForwardReferenceList::append(tail);
    }
  ForwardDoubleReferenceList& append(ForwardList& tail)
    { _beforeBack = NULL;
      return (ForwardDoubleReferenceList&)
        ForwardReferenceList::append(tail);
    }
  list* getBeforeBack() const { return _beforeBack; }
};

class ForwardList {
private:
  list _front;
  list _back;

  friend ForwardReferenceList& ForwardReferenceList::append(ForwardList&);

public:
  ForwardList() : _front(NULL), _back(NULL) {}
  ForwardList(const ForwardList& source)
    : _front(source._front), _back(source._back) {}
  bool isValid() const { return (!_front ? !_back : (_back && !_back->next)); }
  ForwardList& insertPlain(long value)
    { if (_back) {
        assert(!_back->next);
        _back->next = cons_plain(value, NULL);
        _back = _back->next;
      }
      else
        _front = _back = cons_plain(value, NULL);
      return *this;
    }
  ForwardList& insertContainer(void* value)
    { if (_back) {
        assert(!_back->next);
        _back->next = cons_container(value, NULL);
        _back = _back->next;
      }
      else
        _front = _back = cons_container(value, NULL);
      return *this;
    }
  ForwardList& append(const ForwardList& tail)
    { if (_back) {
        if (tail._front) {
          _back->next = tail._front;
          _back = tail._back;
        };
      }
      else {
        _front = tail._front;
        _back = tail._back;
      };
      return *this;
    }
  ForwardList& append(list tail)
    { ForwardReferenceList ftail(tail);
      if (_back) {
        if (tail) {
          _back->next = tail;
          _back = ftail._back;
        };
      }
      else {
        _front = tail;
        _back = ftail._back;
      };
      return *this;
    }
  list getFront() const { return _front; }
  // list getBack() const { return _back; }

  void clear() { _front = _back = NULL; }
};

inline ForwardReferenceList&
ForwardReferenceList::append(ForwardList& tail)
{ if (tail._front) {
    assert(_front);
    if (_back) {
      if (tail._back) {
        _back->next = tail._front;
        _back = tail._back;
      };
    }
    else {
      *_front = tail._front;;
      _back = tail._back;
    };
    tail._front = tail._back = NULL;
  };
  return *this;
}

/** @file */

class ForwardListOption {
private:
  option* _opt;
  ForwardReferenceList _content;

public:
  ForwardListOption() : _opt(NULL), _content() {}
  ForwardListOption(list& content) : _opt(NULL), _content(content) {}
  ForwardListOption(option& aopt): _opt(&aopt), _content()
    { if (aopt->is_some) {
        list l = (list)aopt->content.container;
        _content = ForwardReferenceList(l);
      }
    }
  bool isValid() const
    { return _opt ? ((*_opt)->is_some?_content.isValid():true)
                  : _content.isValid();
    }
  list getCore() const
    { assert(_content.isValid()); return _content.getBack(); }
  ForwardListOption& insertPlain(long value)
    { if (_opt && !(*_opt)->is_some) {
        (*_opt)->is_some = true;
        list* l = (list*)&(*_opt)->content.container;
        _content = ForwardReferenceList(*l);
      }
      _content.insertPlain(value);
      return *this;
    }
  
  ForwardListOption& insertContainer(void* value)
    { if (_opt && !(*_opt)->is_some) {
        (*_opt)->is_some = true;
        list* l = (list*)&(*_opt)->content.container;
        _content = ForwardReferenceList(*l);
      }
      _content.insertContainer(value);
      return *this;
    }
  ForwardReferenceList& getList()
    { if (_opt && !(*_opt)->is_some) {
        (*_opt)->is_some = true;
        list* l = (list*)&(*_opt)->content.container;
        _content = ForwardReferenceList(*l);
      }
      return _content;
    };
  const ForwardReferenceList& getCList() const
    { return _content; }
};

const char* mk_tmp_name ();
const char* mk_materialized_tmp_name ();
bool isRecordOrRecordRef(const clang::Type* type);


#endif //Clang_utilsH