Skip to content
Snippets Groups Projects
ACSLParser.cpp 45.21 KiB
/**************************************************************************/
/*                                                                        */
/*  This file is part of Frama-Clang                                      */
/*                                                                        */
/*  Copyright (C) 2012-2022                                               */
/*    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).                      */
/*                                                                        */
/**************************************************************************/

//
// Description:
//   Implementation of the ACSL++ parser.
//

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#include "clang/Basic/Version.h"
#include "llvm/ADT/SmallString.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/Lookup.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/MacroArgs.h"
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

#include "ACSLParser.h"

namespace Acsl {

// FIXME - should combine these with a varargs argument
// FIXME - want to also allow logic functions with arbitrary numbers of arguments

void Parser::addBuiltinBinding(const std::string& ident, logic_type ret_type, std::initializer_list<logic_type> argtypes) {
  list types = NULL;
  auto iter = argtypes.end();
  while (iter != argtypes.begin()) {
    types = cons_container(logic_arg_decl_cons(*--iter, NULL), types);
  }
    logic_info def =
      logic_info_cons(
        qualified_name_cons(NULL,strdup(ident.c_str())),
        true,
        NULL,NULL,
      ret_type ? opt_some_container(ret_type) : opt_none(),
      types,
        logic_body_LBnone());
    _arguments.addOverloadedLogicFunctions(ident,def);
  }

//void Parser::addBuiltinBinding(const std::string& ident, logic_type ret_type, logic_type arg0) {
//  logic_info def =
//    logic_info_cons(
//      qualified_name_cons(NULL,strdup(ident.c_str())),
//      true,
//      NULL,NULL,
//      ret_type ? opt_some_container(ret_type) : opt_none(),
//      cons_container(
//          logic_arg_decl_cons(arg0,NULL),
//              NULL)
//          ,
//      logic_body_LBnone());
//  _arguments.addOverloadedLogicFunctions(ident,def);
//}
//
//void Parser::addBuiltinBinding(const std::string& ident, logic_type ret_type, logic_type arg0, logic_type arg1 ) {
//  logic_info def =
//    logic_info_cons(
//      qualified_name_cons(NULL,strdup(ident.c_str())),
//      true,
//      NULL,NULL,
//      ret_type ? opt_some_container(ret_type) : opt_none(),
//      cons_container(
//          logic_arg_decl_cons(arg0,NULL),
//          cons_container(
//              logic_arg_decl_cons(arg1,NULL)
//              ,NULL)
//          ),
//      logic_body_LBnone());
//  _arguments.addOverloadedLogicFunctions(ident,def);
//}

void Parser::addBuiltinBindings() {
  logic_type Linteger = logic_type_Linteger();
  logic_type Lreal = logic_type_Lreal();
  logic_type Ldouble = logic_type_Lfloat(FDOUBLE);
  logic_type Lfloat = logic_type_Lfloat(FFLOAT);

  // Set the return type to NULL for predicates
  addBuiltinBinding("\\is_finite",NULL,{Lfloat});
  addBuiltinBinding("\\is_finite",NULL,{Ldouble});
  addBuiltinBinding("\\is_NaN",NULL,{Lfloat});
  addBuiltinBinding("\\is_NaN",NULL,{Ldouble});
  addBuiltinBinding("\\min",Linteger,{Linteger,Linteger});
  addBuiltinBinding("\\max",Linteger,{Linteger,Linteger});
  addBuiltinBinding("\\abs",Linteger,{Linteger});
  addBuiltinBinding("\\abs",Lreal,{Lreal});
  addBuiltinBinding("\\pow",Linteger,{Linteger,Linteger});
  addBuiltinBinding("\\pow",Lreal,{Lreal,Lreal});
  addBuiltinBinding("\\sqrt",Lreal,{Lreal});
  addBuiltinBinding("\\ceil",Lreal,{Lreal});
  addBuiltinBinding("\\floor",Lreal,{Lreal});
  addBuiltinBinding("\\exp",Lreal,{Lreal});
  addBuiltinBinding("\\log",Lreal,{Lreal});
  addBuiltinBinding("\\log10",Lreal,{Lreal});
  addBuiltinBinding("\\sin",Lreal,{Lreal});
  addBuiltinBinding("\\cos",Lreal,{Lreal});
  addBuiltinBinding("\\tan",Lreal,{Lreal});
  addBuiltinBinding("\\sinh",Lreal,{Lreal});
  addBuiltinBinding("\\cosh",Lreal,{Lreal});
  addBuiltinBinding("\\tanh",Lreal,{Lreal});
  addBuiltinBinding("\\asin",Lreal,{Lreal});
  addBuiltinBinding("\\acos",Lreal,{Lreal});
  addBuiltinBinding("\\atan",Lreal,{Lreal});
  addBuiltinBinding("\\asinh",Lreal,{Lreal});
  addBuiltinBinding("\\acosh",Lreal,{Lreal});
  addBuiltinBinding("\\atanh",Lreal,{Lreal});
  addBuiltinBinding("\\atan2",Lreal,{Lreal,Lreal});
  addBuiltinBinding("\\hypot",Lreal,{Lreal,Lreal});
}

void
Parser::Arguments::addErrorMessagesFromLexer()
{ std::list<Lexer::Error>& errorList = _lexer.errorList();
  std::list<Lexer::Error>::const_iterator iterEnd = errorList.end();
  for (std::list<Lexer::Error>::const_iterator iter = errorList.begin();
      iter != iterEnd; ++iter) {
    ++_countErrors;
    if (_errorMessages) {
      std::string message("[lexer error] ");
      message.append(iter->message);
      if (_errorMessages->empty()
          || (_errorMessages->back().getMessage() != message))
        _errorMessages->push_back(ErrorMessage(message,
              iter->filePos, iter->linePos, iter->columnPos));
      if (_doesStopOnError || !_errorMessages || _countErrors >= 20)
        return;
    };
  };
  errorList.clear();
}

void
Parser::Arguments::eatToken(ReadResult& result) {
  _lexer.eatToken(result);
}


const clang::NamedDecl*
Parser::Arguments::isCodeName(const std::string& name,
    const clang::TemplateArgument** templateArgument, bool* hasOverLoad) const
{ if (name.length() == 0)
    return NULL;
#if 0
  if (_clangContext->isFunctionOrMethod()) {
    // Apparently, LookupName does not consider formals in this case...
    const clang::FunctionDecl* f =
      static_cast<const clang::FunctionDecl*>(_clangContext);
    clang::FunctionDecl::param_const_iterator iter = f->param_begin();
    const clang::NamedDecl* param = NULL;
    while (iter!=f->param_end()) {
      const std::string s = (*iter)->getNameAsString();
      if (s == name) {
        param = *iter;
        break;
      }
      iter++;
    }
    if (param) return param;
  }
#endif

#if 0
  // The method clang::DeclContext::lookup seems to fail to find
  // adequate declaration. 
  // std::pair<clang::IdentifierInfo,const unsigned char*> mem;
  // mem.second = (const unsigned char*) name.c_str();
  // clang::DeclarationName delcName(&mem.first);
  
  const clang::DeclContext* currentContext = _clangContext;
  while (currentContext != NULL) {
    // clang::DeclContext::lookup_const_result cdeclResults
    //   = currentContext->lookup(delcName);
    if (currentContext->isRecord()
        && static_cast<const clang::RecordDecl*>(currentContext)->getName()
            .str() == name)
      return static_cast<const clang::RecordDecl*>(currentContext);
    if (currentContext->isNamespace()
        && static_cast<const clang::NamespaceDecl*>(currentContext)->getName()
            .str() == name)
      return static_cast<const clang::NamespaceDecl*>(currentContext);
    clang::DeclContext::decl_iterator iterEnd = currentContext->decls_end();
    for (clang::DeclContext::decl_iterator iter = currentContext->decls_begin();
        iter != iterEnd; ++iter) {
      clang::Decl* decl = *iter;
      clang::Decl::Kind kind = decl->getKind();
      if (kind >= clang::Decl::firstNamed && kind <= clang::Decl::lastNamed) {
        assert(llvm::dyn_cast<clang::NamedDecl>(decl));
        if (static_cast<const clang::NamedDecl*>(decl)->getNameAsString()
            == name)
          return static_cast<const clang::NamedDecl*>(decl);
      };
    };
    // if (cdeclResults.first != cdeclResults.second) {
    //   if (hasOverLoad)
    //     *hasOverLoad = (cdeclResults.first+1 != cdeclResults.second);
    //   return *cdeclResults.first;
    // };
    currentContext = currentContext->getParent();
  };
  // see Sema::CppLookupName(LookupResult &R, Scope *S)
  // for a conform lookup function
#endif

#if 1
  // clang::Token identifierToken;
  // identifierToken.startToken();
  // identifierToken.clearFlag(clang::Token::NeedsCleaning);
  // identifierToken.setIdentifierInfo(0);
  // // std::memset(&identifierToken, 0, sizeof(clang::Token));
  // identifierToken.setLength(name.size());
  // // identifierToken.setLocation(getSourceLocation(BufferPtr, TokLen));
  // identifierToken.setKind(clang::tok::raw_identifier);
  // identifierToken.setRawIdentifierData(name.c_str());
  // clang::IdentifierInfo& identifierInfo = *_clangSema->getPreprocessor()
  //     .LookUpIdentifierInfo(identifierToken);
  clang::IdentifierInfo& identifierInfo
      = _clangSema->getPreprocessor().getIdentifierTable().get(name);
  clang::LookupResult R(*_clangSema, &identifierInfo
      /* &_clangAST->Idents.get(name) */,
      _clangLocation, clang::Sema::LookupOrdinaryName);
  // Argh! The method getScopeForContext uses getCurScope that contains
  // the following comment:
  //   This routine must only be used when it is certain that semantic analysis
  //   and the parser are in precisely the same context, which is not the case
  //   when, e.g., we are performing any kind of template instantiation.
  //   Therefore, the only safe places to use this scope are in the parser
  //   itself and in routines directly invoked from the parser and *never* from
  //   template substitution or instantiation.
  // clang::Scope* scope = _clangSema->getScopeForContext(
  //    const_cast<clang::DeclContext*>(_clangContext));
  if (_clangSema->LookupName(R, _clangScope /* scope */, false)) {
    if (R.getResultKind() == clang::LookupResult::Found)
      return R.getFoundDecl();
    if (hasOverLoad && R.getResultKind()
        == clang::LookupResult::FoundOverloaded)
      *hasOverLoad = true;
  }
  else {
    const clang::NamedDecl* result = NULL;
    const clang::TemplateArgument*
        res = _clang_utils->findTemplateArgument(name, result);
    if (res) {
      if (templateArgument)
        *templateArgument = res;
      return result;
    };
  };
#endif
  return NULL;
}
const clang::NamedDecl*
Parser::Arguments::findQualifiedName(const std::string& name,
    const clang::DeclContext* context, bool* hasOverload) const
{ if (name.length() == 0)
    return NULL;
  clang::LookupResult R(*_clangSema, &_clangAST->Idents.get(name), _clangLocation,
      clang::Sema::LookupOrdinaryName);
  if (_clangSema->LookupQualifiedName(R, const_cast<clang::DeclContext*>(
          context))) {
    if (R.getResultKind() == clang::LookupResult::Found)
      return R.getFoundDecl();
    if (hasOverload
        && R.getResultKind() == clang::LookupResult::FoundOverloaded)
      *hasOverload = true;
  };
  return NULL;
}

template <class TypeTraits>
typename TypeTraits::ResultType
Parser::Arguments::isTypedefType(TypeTraits& traits, qualified_name name) const
{ /* qualification */ list prequalification = name->prequalification;
  const clang::DeclContext*
    declContext = _clangSema->Context.getTranslationUnitDecl();
  while (prequalification) {
    std::string name;
    qualification qualif = (qualification) prequalification->element.container;
    if (qualif->tag_qualification == QNAMESPACE)
      name = qualif->cons_qualification.QNamespace.name;
    else if (qualif->tag_qualification == QSTRUCTORCLASS)
      name = qualif->cons_qualification.QStructOrClass.name;
    else if (qualif->tag_qualification == QTEMPLATEINSTANCE)
      name = qualif->cons_qualification.QTemplateInstance.name;
    else
      { assert(false); }

    clang::LookupResult R(*_clangSema, &_clangAST->Idents.get(name),
        _clangLocation, clang::Sema::LookupOrdinaryName);
    const clang::NamedDecl* found = NULL;
    if (!_clangSema->LookupQualifiedName(R,
          const_cast<clang::DeclContext*>(declContext))) {
      if (name.compare(0, 10, "anonymous_") == 0)
        found = _clang_utils->findAnonymousDecl(name);
      if (!found)
        return TypeTraits::False();
    };
    if (!found && !R.isSingleResult()) // FIXME
      return TypeTraits::False();
    if (!found)
      found = R.getFoundDecl();
    clang::Decl::Kind kind = found->getKind();
    if (kind == clang::Decl::Namespace) {
      assert(llvm::dyn_cast<clang::NamespaceDecl>(found));
      declContext = static_cast<const clang::NamespaceDecl*>(found);
    }
    else if (kind >= clang::Decl::firstTag && kind <= clang::Decl::lastTag) {
      assert(llvm::dyn_cast<clang::TagDecl>(found));
      const clang::TagDecl*
        tagContext = static_cast<const clang::TagDecl*>(found);
      declContext = tagContext;
      if (qualif->tag_qualification == QTEMPLATEINSTANCE) {
        int existingInstancesNumber
          = tagContext->getNumTemplateParameterLists();
        for (int instance = 0; instance < existingInstancesNumber; ++instance) {
          // clang::TemplateParameterList* currentParameters
          //   = tagContext->getTemplateParameterList(instance);
          // TODO: check if qualification matches with currentParameters
          // TODO: find the actual instance of tagContext
          assert(false); // unimplemented
        };
        // TODO: A better alternative is to instantiate via Sema the
        //   CXXRecordDecl that corresponds to tagDecl.
      };
    }
    else if (kind == clang::Decl::ClassTemplate) {
      const clang::TemplateArgumentList* defaultTemplateArgs
          = _clang_utils->findInstanceArguments(found);
      assert(defaultTemplateArgs);
      void* InsertPos = NULL;
      clang::ClassTemplateSpecializationDecl* specialization =
        ((clang::ClassTemplateDecl*) const_cast<clang::NamedDecl*>(found))
        ->findSpecialization(defaultTemplateArgs->asArray(), InsertPos);
      if (!specialization)
         return TypeTraits::False();
      declContext = specialization;
    }
    else
      return TypeTraits::False();
    prequalification = prequalification->next;
  };

  clang::LookupResult R(*_clangSema, &_clangAST->Idents.get(name->decl_name),
      _clangLocation, clang::Sema::LookupOrdinaryName);
  const clang::NamedDecl* found = NULL;
  if (!_clangSema->LookupQualifiedName(R,
        const_cast<clang::DeclContext*>(declContext))) {
    if (strncmp(name->decl_name, "anonymous_", 10) == 0)
      found = _clang_utils->findAnonymousDecl(name->decl_name);
    if (!found)
      return TypeTraits::False();
  }
  if (!found && !R.isSingleResult()) // FIXME
    return TypeTraits::False();
  if (!found)
    found = R.getFoundDecl();
  clang::Decl::Kind kind = found->getKind();
  if (!(kind >= clang::Decl::firstType && kind <= clang::Decl::lastType)
      && kind != clang::Decl::ClassTemplate)
    return TypeTraits::False();
  if (kind == clang::Decl::ClassTemplate) {
    if (!traits.hasTemplateArgs())
      return TypeTraits::False();

    assert(llvm::dyn_cast<clang::ClassTemplateDecl>(found));
    /* template_parameter */ list templateArgs = traits.getTemplateArgs();
    const clang::ClassTemplateDecl* templateDecl
      = static_cast<const clang::ClassTemplateDecl*>(found);
    const clang::ClassTemplateSpecializationDecl* foundSpecialization = NULL;
    if (templateDecl) {
      clang::ClassTemplateDecl::spec_iterator iterEnd
        = const_cast<clang::ClassTemplateDecl*>(templateDecl)->spec_end();
      for (clang::ClassTemplateDecl::spec_iterator iter
          = const_cast<clang::ClassTemplateDecl*>(templateDecl)->spec_begin();
          !foundSpecialization && iter != iterEnd; ++iter) {
        tkind matchTemplateParameters =
          tkind_TTemplateInstance(
            _clang_utils->getTemplateExtension(
              tokenSourceLocation(), iter->getTemplateArgs()));
        struct _tkind templateParameters;
        templateParameters.tag_tkind = TTEMPLATEINSTANCE;
        templateParameters.cons_tkind.TTemplateInstance.parameters
          = templateArgs;
        if (tkind_equal(&templateParameters, matchTemplateParameters))
          foundSpecialization = *iter;
        free_tkind(matchTemplateParameters);
      };
    };
    // void* insertionPos = NULL;
    // const clang::ClassTemplateSpecializationDecl* foundSpecialization =
    //     templateDecl->findSpecialization(traits.getTemplateArgs(),
    //     traits.getNumTemplateArgs(), insertionPos);
    // if (!foundSpecialization)
    //   return TypeTraits::False();
    // return traits(foundSpecialization->getTypeForDecl());
    return traits(foundSpecialization->getTypeForDecl());
  };
  assert(llvm::dyn_cast<clang::TypeDecl>(found));
  const clang::TypeDecl* typeFound = static_cast<const clang::TypeDecl*>(found);
  const clang::Type* type = typeFound->getTypeForDecl();
  if (kind >= clang::Decl::firstTypedefName
      && kind <= clang::Decl::lastTypedefName)
    type = static_cast<const clang::TypedefNameDecl*>(typeFound)
      ->getUnderlyingType().getTypePtr();
  return traits(type);
}

class Parser::Arguments::IsIntegralTypeTraits {
private:
  const Clang_utils* _utils;

public:
  IsIntegralTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  typedef bool ResultType;
  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  static bool False() { return false; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->isIntegralType(type); }
};

class Parser::Arguments::IsSignedTypeTraits {
private:
  const Clang_utils* _utils;

public:
  IsSignedTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef bool ResultType;
  static bool False() { return false; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->isSignedType(type); }
};

class Parser::Arguments::IsArithmeticTypeTraits {
private:
  const Clang_utils* _utils;

public:
  IsArithmeticTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef bool ResultType;
  static bool False() { return false; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->isArithmeticType(type); }
};

class Parser::Arguments::IsFloatingTypeTraits {
private:
  const Clang_utils* _utils;

public:
  IsFloatingTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef bool ResultType;
  static bool False() { return false; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->isFloatingType(type); }
};

class Parser::Arguments::IsPointerTypeTraits {
private:
  const Clang_utils* _utils;

public:
  IsPointerTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef bool ResultType;
  static bool False() { return false; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->isPointerType(type); }
};

class Parser::Arguments::IsReferenceTypeTraits {
private:
  const Clang_utils* _utils;

public:
  IsReferenceTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef bool ResultType;
  static bool False() { return false; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->isReferenceType(type); }
};

class Parser::Arguments::IsArrayTypeTraits {
private:
  const Clang_utils* _utils;

public:
  IsArrayTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef bool ResultType;
  static bool False() { return false; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->isArrayType(type); }
};

class Parser::Arguments::IsCompoundTypeTraits {
private:
  const Clang_utils* _utils;
  tkind* _templateKind;

public:
  IsCompoundTypeTraits(const Clang_utils* utils, tkind* templateKind)
    : _utils(utils), _templateKind(templateKind) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }
  typedef qualified_name ResultType;
  static qualified_name False() { return NULL; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->makeCompoundType(type, _templateKind); }
};

class Parser::Arguments::GetRecordTypeTraits {
private:
  const Clang_utils* _utils;

public:
  GetRecordTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef const clang::RecordType* ResultType;
  static const clang::RecordType* False() { return NULL; }
  ResultType operator()(const clang::Type* type) const
    { ResultType result = NULL;
      if (type->getTypeClass() == clang::Type::Typedef) {
        assert(llvm::dyn_cast<clang::TypedefType>(type));
        type = static_cast<const clang::TypedefType*>(type)->desugar()
          .getTypePtr();
      };
      if (type->getTypeClass() == clang::Type::Record) {
        assert(llvm::dyn_cast<clang::RecordType>(type));
        result = static_cast<ResultType>(type);
      }
      return result;
    }
};

class Parser::Arguments::LogicArithmeticPromotionTraits {
private:
  const Clang_utils* _utils;

public:
  LogicArithmeticPromotionTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef logic_type ResultType;
  static logic_type False() { return NULL; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->logicArithmeticPromotion(clang::SourceLocation(),type); }
  // FIXME - what about this source location? and elsewhere in this file?
};

class Parser::Arguments::MakePointedTypeTraits {
private:
  const Clang_utils* _utils;

public:
  MakePointedTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef logic_type ResultType;
  static logic_type False() { return NULL; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->makePointedType(clang::SourceLocation(),type); }
};

class Parser::Arguments::MakeReferencedTypeTraits {
private:
  const Clang_utils* _utils;
public:
  MakeReferencedTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef logic_type ResultType;
  static logic_type False() { return NULL; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->makeReferencedType(clang::SourceLocation(),type); }
};

class Parser::Arguments::MakeElementArrayTypeTraits {
private:
  const Clang_utils* _utils;

public:
  MakeElementArrayTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef logic_type ResultType;
  static logic_type False() { return NULL; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->makeElementArrayType(clang::SourceLocation(),type); }
};

class Parser::Arguments::MakeLogicTypeTraits {
private:
  const Clang_utils* _utils;

public:
  MakeLogicTypeTraits(const Clang_utils* utils) : _utils(utils) {}

  bool hasTemplateArgs() const { return false; }
  /* template_parameter */ list getTemplateArgs() const { return NULL; }

  typedef logic_type ResultType;
  static logic_type False() { return NULL; }
  ResultType operator()(const clang::Type* type) const
    { return _utils->makeLogicType(clang::SourceLocation(),type); }
};

bool
Parser::Arguments::isIntegralTypedefType(qualified_name name) const
{ IsIntegralTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

bool
Parser::Arguments::isSignedTypedefType(qualified_name name) const
{ IsSignedTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

bool
Parser::Arguments::isArithmeticTypedefType(qualified_name name) const
{ IsArithmeticTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

bool
Parser::Arguments::isFloatingTypedefType(qualified_name name) const
{ IsFloatingTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

bool
Parser::Arguments::isPointerTypedefType(qualified_name name) const
{ IsPointerTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

bool
Parser::Arguments::isReferenceTypedefType(qualified_name name) const
{ IsReferenceTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

bool
Parser::Arguments::isArrayTypedefType(qualified_name name) const
{ IsArrayTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

bool Parser::Arguments::is_wchar_signed() const {
  return
    clang::TargetInfo::isTypeSigned(_clangAST->getTargetInfo().getWCharType());
}

bool Parser::Arguments::is_char_signed() const {
  return _clangAST->getLangOpts().CharIsSigned;
}

qualified_name
Parser::Arguments::makeCompoundTypedefType(qualified_name name,
    tkind* templateKind) const
{ IsCompoundTypeTraits traits(_clang_utils, templateKind);
  return isTypedefType(traits, name);
}

logic_type
Parser::Arguments::makeTypeOfPointed(qualified_name name) const
{ MakePointedTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

logic_type
Parser::Arguments::makeTypeOfReferenced(qualified_name name) const
{ MakeReferencedTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

logic_type
Parser::Arguments::makeTypeOfArrayElement(qualified_name name) const
{ MakeElementArrayTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

const clang::RecordType*
Parser::Arguments::getRecordType(qualified_name name) const
{ GetRecordTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

GlobalContext::NestedContext*
Parser::Arguments::findLogicName(qualified_name context,
    const std::string& name, tkind* templateParameterContext) const {
  GlobalContext::NestedContext* logicContext
      = _clang_utils->globalAcslContext().findAbsolute(context);
  if (logicContext) {
    if (templateParameterContext && *templateParameterContext
        && (*templateParameterContext)->tag_tkind == TTEMPLATEINSTANCE) {
      if (logicContext->isQualification() && logicContext->asQualification()
            .hasTemplateRecordType())
        logicContext = logicContext->asQualification().findInstance(
          (*templateParameterContext)->cons_tkind.TTemplateInstance.parameters);
      else
        logicContext = NULL;
    };
  };
  return logicContext
    ? _clang_utils->globalAcslContext().find(name, logicContext)
    : NULL;
}

logic_type
Parser::Arguments::logicArithmeticPromotion(qualified_name name) const
{ LogicArithmeticPromotionTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

logic_type
Parser::Arguments::makeLogicType(qualified_name name) const
{ MakeLogicTypeTraits traits(_clang_utils);
  return isTypedefType(traits, name);
}

qualified_name
Parser::Arguments::makeLogicQualifiedName(const std::string& name) const {
  const clang::DeclContext* ctxt = _clangContext;
  return _clang_utils->makeQualifiedName(ctxt, copy_string(name));
}

logic_type
Parser::Arguments::queryThisType() const {
  const clang::DeclContext* currentContext = _clangContext;
  while ((currentContext != NULL) && !currentContext->isRecord())
    currentContext = currentContext->getParent();
  if (!currentContext)
    return NULL;
  /* qualification */ list prequalification = NULL;
  const clang::DeclContext* qualContext = currentContext->getParent();
  while (qualContext != NULL) {
    if (qualContext->isRecord()) {
      const clang::RecordDecl* decl = static_cast<const clang::RecordDecl*>
          (qualContext);
      qualification res = NULL;
      if (decl->getKind() == clang::Decl::ClassTemplateSpecialization) {
        const clang::ClassTemplateSpecializationDecl* TSD
            = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(decl);
        res = qualification_QTemplateInstance(
          strdup(decl->getName().str().c_str()),
          _clang_utils->getTemplateExtension(
            tokenSourceLocation(),TSD->getTemplateArgs()));
      }
      else
        res = qualification_QStructOrClass(
          strdup(decl->getName().str().c_str()));
      prequalification = cons_container(res, prequalification);
    }
    else if (qualContext->isNamespace())
      prequalification = cons_container(qualification_QNamespace(
        strdup(static_cast<const clang::NamespaceDecl*>(qualContext)
          ->getName().str().c_str())), prequalification);
    qualContext = qualContext->getParent();
  };
  const clang::RecordDecl* decl = static_cast<const clang::RecordDecl*>
      (currentContext);
  tkind templateParameters = NULL;
  if (decl->getKind() == clang::Decl::ClassTemplateSpecialization) {
    const clang::ClassTemplateSpecializationDecl* TSD
        = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(decl);
    templateParameters =
      tkind_TTemplateInstance(
        _clang_utils->getTemplateExtension(
          tokenSourceLocation(),TSD->getTemplateArgs()));
  }
  else
    templateParameters = tkind_TStandard();
  const clang::RecordDecl* record =
    static_cast<const clang::RecordDecl*>(currentContext);
  qualified_name rec_name =
    qualified_name_cons(
      prequalification, strdup(record->getName().str().c_str()));
  logic_type classType =
    record->isUnion() ?
    logic_type_Lunion(rec_name, templateParameters):
    logic_type_Lstruct(rec_name, templateParameters);
  if (isGlobalAnnotContext()) return classType;
  else return logic_type_Lpointer(classType);
}

term_lval Parser::Arguments::thisLval () const {
  logic_type thisType = queryThisType();
  if (!thisType) return NULL;
  term_lval res = NULL;
  if (thisType->tag_logic_type == LPOINTER) {
    term_lval thisPtr =
      term_lval_cons(
        term_lhost_TVar(
          logic_var_cons(qualified_name_cons(NULL, strdup("this")), LVFORMAL)),
        term_offset_TNoOffset());
    res =
      term_lval_cons(
        term_lhost_TMem(
          term_cons(term_node_TLval(thisPtr), newTokenLocation(), NULL)),
        term_offset_TNoOffset());
  } else {
    res = 
      term_lval_cons(
        term_lhost_TVar(
          logic_var_cons(qualified_name_cons(NULL,strdup("\\this")), LVFORMAL)),
        term_offset_TNoOffset());
  }
  free_logic_type(thisType);
  return res;
}

class Parser::Arguments::RetrieveTypeOfFieldTraits {
private:
  const Clang_utils* _utils;
  const RTTITable* _rttiTable;
  std::string _fieldName;
  term_offset& _offset;
  logic_type& _ltype;
  tkind _templateParameters;
  std::string _errorMessage;
  const clang::ASTContext* _clangAST;
  clang::Sema* _clangSema;
  clang::SourceLocation _location;

public:
  RetrieveTypeOfFieldTraits(const Clang_utils* utils,const RTTITable* rttiTable,
      const std::string& fieldName, term_offset& offset, logic_type& ltype,
      tkind templateParameters, const clang::ASTContext* clangAST,
      clang::Sema* clangSema, clang::SourceLocation location)
    : _utils(utils), _rttiTable(rttiTable), _fieldName(fieldName),
      _offset(offset), _ltype(ltype), _templateParameters(templateParameters),
      _clangAST(clangAST), _clangSema(clangSema), _location(location) {}

  bool hasTemplateArgs() const
    { return _templateParameters->tag_tkind == TTEMPLATEINSTANCE; }
  /* template_parameter */ list getTemplateArgs() const
    { return _templateParameters->cons_tkind.TTemplateInstance.parameters; }
  const std::string& errorMessage() const { return _errorMessage; }

  typedef bool ResultType;
  static bool False() { return false; }
  ResultType operator()(const clang::Type* type)
    { return _utils->retrieveTypeOfField(type, _fieldName, _offset, _ltype,
        _errorMessage, _clangAST, _clangSema, _location, *_rttiTable);
    }
};

bool
Parser::Arguments::retrieveTypeOfField(qualified_name name,
    tkind templateParameters, const std::string& fieldName,
    term_offset& offset, logic_type& ltype, std::string& errorMessage)
{ RetrieveTypeOfFieldTraits traits(_clang_utils, &_rttiTable, fieldName,
      offset, ltype, templateParameters, _clangAST, _clangSema, _clangLocation);
  bool result = isTypedefType(traits, name);
  if (!result)
    errorMessage = traits.errorMessage();
  return result;
}

bool
Parser::Arguments::addOverloadedLogicFunctionOrOperator(const std::string& name,
    logic_info info, bool isMethod,
    DLexer::OperatorPunctuatorToken::Type* codeOperator) {
  GlobalContext::NestedContext::SonsSet* localScope = NULL;
  if (_currentLogicScope) {
    localScope = _currentLogicScope->ssons();
    if (!localScope && _currentLogicScope->sparent())
      localScope = _currentLogicScope->sparent()->ssons();
  }
  if (!localScope)
    localScope = &_clang_utils->globalAcslContext().logicTable();
  GlobalContext::NestedContext locateContext(name);
  GlobalContext::NestedContext::SonsSet::iterator
    found = localScope->find(&locateContext);
  if (found == localScope->end()) {
    if (!codeOperator)
      localScope->insert(
          new GlobalContext::OverloadedLogicFunctions(name, info, isMethod));
    else
      localScope->insert(new GlobalContext::OverloadedLogicOperators(name,
            *codeOperator, info, isMethod));
  }
  else if (!(*found)->isOverloadedLogicFunctions())
    return false;
  else {
    typedef GlobalContext::OverloadedLogicFunctions::Functions Functions;
    const Functions& existingInfos
      = (*found)->asOverloadedLogicFunctions().getFunctions();
    Functions::const_iterator iterEnd = existingInfos.end();
    for (Functions::const_iterator iter = existingInfos.begin();
        iter != iterEnd; ++iter) {
      logic_info sourceInfo = iter->second;

      std::map<std::string, std::string> renaming;
      bool isEqual = true;
      if (sourceInfo->tparams) {
        if (!info->tparams)
          isEqual = false;
        /* const char* */ list infoParamNames = info->tparams,
                        sourceInfoParamNames = sourceInfo->tparams;
        while (isEqual && infoParamNames && sourceInfoParamNames) {
          renaming.insert(std::pair<std::string, std::string>(
            (const char*) sourceInfoParamNames->element.container,
            (const char*) infoParamNames->element.container));
          infoParamNames = infoParamNames->next;
          sourceInfoParamNames = sourceInfoParamNames->next;
        };
        if (infoParamNames || sourceInfoParamNames)
          isEqual = false;
      };
      isEqual = isEqual
        || (info->returned_type->is_some == sourceInfo->returned_type->is_some);
      if (isEqual && info->returned_type->is_some)
        isEqual = logic_type_equal(
            (logic_type) sourceInfo->returned_type->content.container,
            (logic_type) info->returned_type->content.container);
      if (isEqual) {
        /* logic_arg_decl */ list args = info->profile,
                                  sourceArgs = sourceInfo->profile;
        while (isEqual && args && sourceArgs) {
          isEqual = logic_type_equal(
            ((logic_arg_decl) args->element.container)->la_type,
            ((logic_arg_decl) sourceArgs->element.container)->la_type);
          args = args->next;
          sourceArgs = sourceArgs->next;
        };
        isEqual = isEqual && (args == NULL) && (sourceArgs == NULL);
      };
      if (isEqual) {
        if (sourceInfo->fun_body->tag_logic_body == LBNONE) {
          free_logic_body(sourceInfo->fun_body);
          sourceInfo->fun_body = info->fun_body;
          info->fun_body = logic_body_LBnone();
        }
        else if (info->fun_body->tag_logic_body == LBNONE)
          return false;
        if (!sourceInfo->arg_labels) {
          sourceInfo->arg_labels = info->arg_labels;
          info->arg_labels = NULL;
        }
        else {
          while (info->arg_labels) {
            /* logic_label */ list sourceLabelIter = sourceInfo->arg_labels;
            do {
              if (logic_label_equal(
                    (logic_label) sourceLabelIter->element.container,
                    (logic_label) info->arg_labels->element.container))
                break;
              sourceLabelIter = sourceLabelIter->next;
            } while (sourceLabelIter);
            if (!sourceLabelIter) {
              sourceInfo->arg_labels = cons_container((logic_label) info
                  ->arg_labels->element.container, sourceInfo->arg_labels);
              /* logic_label */ list cell = info->arg_labels;
              info->arg_labels = info->arg_labels->next;
              free(cell);
            }
            else {
              /* logic_label */ list cell = info->arg_labels;
              free_logic_label((logic_label)
                  info->arg_labels->element.container);
              info->arg_labels = info->arg_labels->next;
              free(cell);
            };
            info->arg_labels = info->arg_labels->next;
          };
        };
        return true;
      }
    };
    (*found)->asOverloadedLogicFunctions().addFunction(info, isMethod);
  };
  return true;
}
 
bool
Parser::Arguments::addLogicType(const std::string& name, logic_type_info
    ltypeInfo) {
  GlobalContext::NestedContext::SonsSet* localScope = NULL;
  if (_currentLogicScope) {
    localScope = _currentLogicScope->ssons();
    if (!localScope && _currentLogicScope->sparent())
      localScope = _currentLogicScope->sparent()->ssons();
  }
  if (!localScope)
    localScope = &_clang_utils->globalAcslContext().logicTable();
  GlobalContext::NestedContext locateContext(name);
  GlobalContext::NestedContext::SonsSet::iterator
    found = localScope->find(&locateContext);
  if (found != localScope->end())
    return false;

  localScope->insert(new GlobalContext::LogicType(name, ltypeInfo));
  return true;
}

GlobalContext::LogicType*
Parser::Arguments::findGlobalLogicType(qualified_name name) const {
  GlobalContext::NestedContext* result
    = _clang_utils->globalAcslContext().findAbsolute(name);
  return (result && result->isLogicType()) ? &result->asLogicType() : NULL;
}

/* qualification */ list
Parser::Arguments::createPrequalification() const {
  /* qualification */ list result = NULL;
  GlobalContext::NestedContext* scope = _currentLogicScope;
  while (scope) {
    if (scope->isQualification()) {
      result = cons_container(scope->asQualification().getQualification(),
          result);
      scope = scope->sparent();
    }
    else if (scope->isTemplateQualification()) {
      GlobalContext::NestedContext* rightParent = scope->sparent();
      assert(rightParent && rightParent->isQualification());
      result = cons_container(scope->asTemplateQualification()
          .getQualification(strdup(rightParent->asQualification()
            .getName().c_str())), result);
      scope = rightParent->sparent();
    }
    else
      assert(false);
  };
  return result;
}

GlobalContext::LogicVariable*
Parser::Arguments::findGlobalLogicVariable(qualified_name name) const {
  GlobalContext::NestedContext* result
    = _clang_utils->globalAcslContext().findAbsolute(name);
  return (result && result->isLogicVariable())
    ? &result->asLogicVariable() : NULL;
}


logic_label
Parser::Arguments::findLogicLabel(const std::string& name) const {
  std::set<std::string>::iterator found = _logicLabels.find(name);
  if (found != _logicLabels.end())
    return logic_label_LogicLabel(strdup(found->c_str()));

  clang::IdentifierInfo& identifierInfo
      = _clangSema->getPreprocessor().getIdentifierTable().get(name);
  clang::NamedDecl* foundDecl = _clangSema->LookupSingleName(
      _clangScope /* scope */, &identifierInfo, _clangLocation,
      clang::Sema::LookupLabel, clang::Sema::NotForRedeclaration);
  if (!foundDecl || (foundDecl->getKind() != clang::Decl::Label))
    return NULL;
  assert(llvm::dyn_cast<clang::LabelDecl>(foundDecl));
  return logic_label_StmtLabel(strdup(foundDecl->getName().str().c_str()));
}

logic_type
Parser::Arguments::createResultType() const {
  const clang::DeclContext* context = _clangContext;
  clang::Decl::Kind declKind;
  if (!_result_context) return NULL;
  while (context
      && !((declKind = context->getDeclKind()) >= clang::Decl::firstFunction
      && declKind <= clang::Decl::lastFunction))
    context = context->getParent();
  if (!context)
    return NULL;
  assert(llvm::dyn_cast<clang::FunctionDecl>(context));
  const clang::FunctionDecl*
    functionDecl = static_cast<const clang::FunctionDecl*>(context);
  const clang::SourceLocation loc = tokenSourceLocation();
  return
    _clang_utils->makeLogicType(loc,functionDecl->getReturnType().getTypePtr());
}

void
Parser::parse(const std::string& buffer, const clang::SourceLocation& sourceLocation) {
  lexer().setBuffer(buffer, sourceLocation);
  _arguments._tokenLocation = lexer().seeTokenLocation();
  _arguments._clangLocation = sourceLocation;
//  _tokenLocation->linenum2 = __tokenLocation->linenum1;
//  _tokenLocation->charnum2 = _tokenLocation->charnum1-1;
  ReadResult parseResult = lexer().readToken();
  while (parseResult != RRFinished) {
    if (parseResult == RRNeedChars) {
        parseResult = _arguments.lexer().readToken();
        if (_arguments.lexer().hasErrors())
          _arguments.addErrorMessagesFromLexer();
      if (parseResult == RRFinished)
        break;
      if (parseResult == RRNeedChars)
        break; // could add characters to buffer
    };
 
    if (!_errorMessages.empty()) {
      std::list<ErrorMessage>::const_iterator iter = _errorMessages.begin();
      while(iter != _errorMessages.end()) {
        std::cerr << iter->getMessage() << std::endl;
        iter++;
      }
      _errorMessages.clear();
      if (_arguments.get_clang_utils()->stopOnAnnotError())
        exit(2);
    }

    while (parseResult == RRContinueLexing)
      parseResult = _arguments.lexer().readToken();
    if (parseResult == RRHasToken) {
      DLexer::AbstractToken::Type type = _arguments.queryToken().getType();
//      // This test is to be removed!
//      // The preprocessor should be called by the first pass
//      //   with the comment handler!
//      if (type == DLexer::AbstractToken::TIdentifier) {
//        if (_arguments.isPreprocessingDirective(
//            ((const DLexer::IdentifierToken&) _arguments.getContentToken())
//              .content())) {
//          std::string identifier = ((const DLexer::IdentifierToken&)
//            _arguments.getContentToken()).content();
//          clang::MacroInfo* macro = _arguments.getMacro(identifier);
//          if (_arguments._usedMacros.find(identifier)
//              != _arguments._usedMacros.end())
//            parseResult = RRHasToken;
//          else if (_arguments.handleMacroExpandedIdentifier(identifier, macro,
//                parseResult)) {
//            parseResult = RRNeedChars;
//            continue;
//          };
//        };
//      };
      if (parseResult == RRHasToken && type != DLexer::AbstractToken::TComment) {
        do {
          parseResult = _state.parse(_arguments);
          if (_arguments.hasErrors()) {
            std::list<ErrorMessage>& errors = _arguments.errors();
            std::list<ErrorMessage>::const_iterator iterEnd = errors.end();
            for (std::list<ErrorMessage>::const_iterator iter = errors.begin();
                iter != iterEnd; ++iter)
              std::cerr << iter->filepos() << ':' << iter->linepos() << ':'
                << iter->columnpos() << ": " << iter->getMessage() << std::endl;
            errors.clear();
          };
        } while (parseResult == RRContinueParsing);
//        if (parseResult == RRFinished && !lexer().hasFinished()) {
//          location loc = lexer().seeTokenLocation();
//          std::cerr << loc->filename1 << ":" << loc->linenum1 << ":" << loc->charnum1 << ": " << "Parser terminated before the end of input" << std::endl;
//        }
      }
      else
        parseResult = RRNeedChars;
      if (parseResult == RRNeedChars && _arguments.lexer().doesNeedClear())
        _arguments.lexer().clearToken();
    };
  };
}

bool
Parser::Arguments::addErrorMessage(const std::string& message, location tokenLocation)
  { ++_countErrors;
    if (_errorMessages && (_errorMessages->empty()
        || (_errorMessages->back().getMessage() != message))) {
      location tokenLocation = _lexer.seeTokenLocation();
      _errorMessages->push_back(ErrorMessage(message,
          tokenLocation->filename1, tokenLocation->linenum1,
          tokenLocation->charnum1));
    }
    return !doesStopAfterTooManyErrors();
  }


} // end of namespace Acsl