Skip to content
Snippets Groups Projects
Forked from pub / Frama Clang
487 commits behind the upstream repository.
ClangVisitor.cpp 379.97 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:
//   Definition of a translator clang -> intermediate_format (-> cabs -> cil).
//
extern "C" {
#include "intermediate_format.h"
};
#include <clang/AST/Expr.h>
#include <clang/AST/ExprCXX.h>
#include <llvm/Support/Casting.h>
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/APValue.h"
#include "clang/Basic/FileManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/Utils.h"
#include "clang/Sema/Scope.h"
#include "Clang_utils.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Host.h"
#include "llvm/ADT/ArrayRef.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstddef>
#include <list>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

#include "ClangVisitor.h"

void
AnnotationCommentList::add(AnnotationComment* comment,
    clang::SourceManager &sourceManager) {
  if (!comment || !comment->isValid())
    return;

  // Check if the comments are not in source order.
  while (!empty()
      && !sourceManager.isBeforeInTranslationUnit(
          back()->getSourceRange().getBegin(),
          comment->getSourceRange().getBegin())) {
    // If they are, just pop a few last comments that don't fit.
    // This happens if an \#include directive contains comments.
    back()->freeContent();
    delete back();
    pop_back();
  }
  push_back(comment);
}

std::unique_ptr<clang::ASTConsumer>
FramaCIRGenAction::CreateASTConsumer(clang::CompilerInstance& CI,
    clang::StringRef InFile)
{ FramacVisitor* vis = new FramacVisitor(_outFile, _compilerInstance);
  if (_annotError) vis->setAnnotError();
  if (_doesGenerateImplicitMethods) vis->setGenerateImplicitMethods();
  if (_isVerbose) vis->setVerbose();
  return std::unique_ptr<clang::ASTConsumer>(vis);
}

/**************************************************************/
/* Implementation of the class FramaCIRGenAction::DeclContext */
/**************************************************************/

void
FramaCIRGenAction::DeclContext::reopenLexicalHierarchy(
    FramacVisitor& visitor) {
  std::list<LexicalLocalDeclContext>::iterator
    iter = _lexicalParent.begin(),
    iterEnd = _lexicalParent.end();
  if (iter != iterEnd) {
    assert(iter->isNamespace());
    assert(llvm::dyn_cast<const clang::NamespaceDecl>(iter->getSynchro()));
    const clang::NamespaceDecl* namespaceDecl
      = static_cast<const clang::NamespaceDecl*>(iter->getSynchro());
    char* name=const_cast<char*>(copy_string(namespaceDecl->getName().str()));
    location loc = visitor.makeDeclLocation(*namespaceDecl);
    translation_unit_decl decl
      = translation_unit_decl_Namespace(loc, name, NULL);
    visitor._globals.insertContainer(decl);
    /* translation_unit_decl */ list* namespaceElements
      = &decl->cons_translation_unit_decl.Namespace.body;
    iter->resetNamespace(*namespaceElements, iter->getSynchro());
    LexicalLocalDeclContext* previous = &*iter;
    for (++iter; iter != iterEnd; ++iter) {
      assert(iter->isNamespace());
      assert(llvm::dyn_cast<const clang::NamespaceDecl>(iter->getSynchro()));
      const clang::NamespaceDecl* namespaceDecl
        = static_cast<const clang::NamespaceDecl*>(iter->getSynchro());
      char* name=const_cast<char*>(copy_string(namespaceDecl->getName().str()));
      location loc = visitor.makeLocation(namespaceDecl->getSourceRange());
      translation_unit_decl decl
        = translation_unit_decl_Namespace(loc, name, NULL);
      assert(!*namespaceElements);
      *namespaceElements = cons_container(decl, NULL);
      previous->getDeclarations().advanceToEnd();
      namespaceElements = &decl->cons_translation_unit_decl.Namespace.body;
      iter->resetNamespace(*namespaceElements, iter->getSynchro());
      previous = &*iter;
    };
  };
}

void
FramaCIRGenAction::DeclContext::pushFunctionBody(
    clang::FunctionDecl* functionDecl)
{ // see Sema::ActOnStartOfFunctionDef
  clang::Scope* scope = new clang::Scope(_scope,
    clang::Scope::FnScope | clang::Scope::DeclScope, _diags);
  _scope = scope;
  _sema->PushFunctionScope();
  // _sema->PushDeclContext(scope, functionDecl);
  _sema->EnterDeclaratorContext(scope, functionDecl);
  scope->AddDecl(functionDecl);
  for (unsigned p=0, NumParams = functionDecl->getNumParams();
      p < NumParams; ++p) {
    clang::ParmVarDecl *Param = functionDecl->getParamDecl(p);
    // If this has an identifier, add it to the scope stack.
    if (Param->getIdentifier())
      // scope->AddDecl(Param);
      _sema->PushOnScopeChains(Param, _scope, false);
  }
  pushCompoundScope();
  _currentFunctionBody = functionDecl;
}

void
FramaCIRGenAction::DeclContext::popFunctionBody(
    clang::FunctionDecl* functionDecl)
{ clang::Scope* scope = _scope->getParent();
  _sema->PopCompoundScope();
  _sema->ExitDeclaratorContext(_scope);
  assert(scope);
  delete _scope;
  _scope = scope;
  // _sema->PopDeclContext();
  _sema->PopFunctionScopeInfo(NULL, functionDecl);
  _currentFunctionBody = NULL;
}

void
FramaCIRGenAction::DeclContext::setLocalPush(
    const clang::DeclContext* parentDecl) {
  std::list<const clang::DeclContext*> reverseDecl;
  assert(!_semanticParent.empty());
  while (parentDecl && parentDecl != _semanticParent.back().getSynchro()) {
    reverseDecl.push_back(parentDecl);
    parentDecl = parentDecl->getParent();
  };
  assert(parentDecl && _localPush == 0);
  std::list<const clang::DeclContext*>::const_reverse_iterator
    iterEnd = reverseDecl.rend();
  for (std::list<const clang::DeclContext*>::const_reverse_iterator
      iter = reverseDecl.rbegin(); iter != iterEnd; ++iter) {
    _semanticParent.push_back(_semanticParent.back().findDecl(*iter));
    clang::Scope* scope = new clang::Scope(_scope, 0, _diags);
    _scope = scope;
    _sema->EnterDeclaratorContext(_scope,
        const_cast<clang::DeclContext*>(_semanticParent.back().getSynchro()));
    ++_localPush;
  };
}

/*
void
FramaCIRGenAction::DeclContext::setLocalPushInstantiation(
    option& declarations, const clang::DeclContext* classContext) {
  assert(_localPush == 0);
  bool hasFound = false;
  if (!_semanticParent.empty()) {
    LocalDeclContext found = _semanticParent.back().findDecl(classContext);
    if ((hasFound = found.isValid()) != false)
      _semanticParent.push_back(found);
  };
  if (!hasFound) {
    assert(classContext->isRecord());
    pushSemanticClass(declarations, classContext);
  };

  clang::Scope* scope = new clang::Scope(_scope, 0, _diags);
  _scope = scope;
  _sema->EnterDeclaratorContext(_scope,
      const_cast<clang::DeclContext*>(classContext));
  ++_localPush;
}
*/

void
FramaCIRGenAction::DeclContext::clearLocalPush() {
  while (_localPush > 0) {
    clang::Scope* scope = _scope->getParent();
    _sema->ExitDeclaratorContext(_scope);
    assert(scope);
    delete _scope;
    _scope = scope;
    _semanticParent.pop_back();
    --_localPush;
  };
}

void
FramaCIRGenAction::DeclContext::lexicalSynchronizeWith(
    const clang::DeclContext* parentDecl, FramacVisitor& visitor) {
  if (!parentDecl || !parentDecl->getLexicalParent()
      || parentDecl->getDeclKind()==clang::Decl::LinkageSpec) {
    size_t nbPops = _lexicalParent.size();
    if (nbPops > 0) {
      do {
        _lexicalParent.back().clear();
        _lexicalParent.pop_back();
      } while (--nbPops);
      assert(_lexicalParent.empty());
    };
  }
  else {
    assert(!_lexicalParent.empty());
    while (!parentDecl->Equals(_lexicalParent.back().getSynchro())) {
      _lexicalParent.back().clear();
      _lexicalParent.pop_back();
      if (_lexicalParent.empty()) {
         const clang::DeclContext* cursorDecl = parentDecl->getLexicalParent();
         while (cursorDecl->getLexicalParent()
             && (cursorDecl->getDeclKind()==clang::Decl::LinkageSpec))
           cursorDecl = cursorDecl->getLexicalParent();
         assert(!cursorDecl->getLexicalParent()
             && Clang_utils::isExternCContext(parentDecl));
         return;
      };
    };
  };
}

void
FramaCIRGenAction::DeclContext::semanticSynchronizeWith(
    const clang::DeclContext* parentDecl, FramacVisitor& visitor) {
  if (!parentDecl || !parentDecl->getParent()
      || parentDecl->getDeclKind()==clang::Decl::LinkageSpec) {
    size_t nbPops = _semanticParent.size();
    if (nbPops > 0) {
      do {
        _semanticParent.back().clear();
        _semanticParent.pop_back();
        clang::Scope* scope = _scope->getParent();
        _sema->ExitDeclaratorContext(_scope);
        assert(scope);
        delete _scope;
        _scope = scope;
        // _sema->PopDeclContext();
      } while (--nbPops);
      assert(_semanticParent.empty());
    };
  }
  else {
    assert(!_semanticParent.empty());
    while (!parentDecl->Equals(_semanticParent.back().getSynchro())) {
      clang::Scope* scope = _scope->getParent();
      _sema->ExitDeclaratorContext(_scope);
      assert(scope);
      delete _scope;
      _scope = scope;
      _semanticParent.back().clear();
      _semanticParent.pop_back();
      if (_semanticParent.empty()) {
         const clang::DeclContext* cursorDecl = parentDecl->getParent();
         while (cursorDecl->getParent()
             && (cursorDecl->getDeclKind()==clang::Decl::LinkageSpec))
           cursorDecl = cursorDecl->getParent();
         assert(!cursorDecl->getParent()
             && Clang_utils::isExternCContext(parentDecl));
         return;
      };
    };
  };
}

void
FramaCIRGenAction::DeclContext::clearLexical(FramacVisitor& visitor) {
  size_t nbPops = _lexicalParent.size();
  if (nbPops > 0) {
    do {
      _lexicalParent.back().clear();
      _lexicalParent.pop_back();
    } while (--nbPops);
    assert(_lexicalParent.empty());
  };
}

void
FramaCIRGenAction::DeclContext::clearSemantic(FramacVisitor& visitor) {
  size_t nbPops = _semanticParent.size();
  if (nbPops > 0) {
    do {
      _semanticParent.back().clear();
      _semanticParent.pop_back();
      clang::Scope* scope = _scope->getParent();
      _sema->ExitDeclaratorContext(_scope);
      assert(scope);
      delete _scope;
      _scope = scope;
      // _sema->PopDeclContext();
    } while (--nbPops);
    assert(_semanticParent.empty());
  };
}

/********************************************************/
/* Implementation of the class AnnotationCommentHandler */
/********************************************************/

bool
FramaCIRGenAction::AnnotationCommentHandler::HandleComment(
    clang::Preprocessor &PP, clang::SourceRange commentRange) {
  if (!_sourceManager)
    _sourceManager = &_compilerInstance.getSourceManager();
  if (!_sema) _sema = &_compilerInstance.getSema();

  AnnotationComment* comment = AnnotationCommentFactory
    ::createAnnotationComment(*_sourceManager, commentRange, _sema);
  if (!comment || !comment->isValid()) {
    if (comment) {
      comment->freeContent();
      delete comment;
    };
    return false;
  };
  _visitor._annotationCommentList.add(comment, *_sourceManager);
  return false;
}

/***************************************/
/* Implementation of the class Visitor */
/***************************************/

FramacVisitor::FramacVisitor(
  FILE* outstream, clang::CompilerInstance& compilerInstance)
  : _outFile(NULL), _sema(NULL), 
    _parents(compilerInstance.getDiagnostics()), _state(SSynchronized),
    _context(NULL), _implicitThisStar(NULL),
    _isImplicitThisBare(false),
    _ghost(false), _ghost_set(),
    _commentHandler(compilerInstance, *this), _commentIndex(-1),
    _templateCommentIndex(-2), _lexicalContextForPostVisit(NULL),
    _semanticContextForPostVisit(NULL),
    _typeFunctionResult(NULL),
    _generatedVaList(false),
    _templateContextsNumber(0)
{ _outFile = outstream;
  _clangUtils = new Clang_utils(NULL, this);
  _intermediateAST = (file)malloc(sizeof(*_intermediateAST));
  _intermediateAST->globals=NULL;
  _commentHandler.compilerInstance().getPreprocessor()
      .addCommentHandler(&_commentHandler);
  _globals = ForwardDoubleReferenceList(_intermediateAST->globals);
  _tableForWaitingDeclarations.setUtils(_clangUtils);
  struct _location loc = {"", 0, 0, "", 0, 0};
  _rttiTable.insertVMTAndRttiPrelude(_globals, &loc);
  memset(_generatedBuiltins, 0, SizeOfBuiltinArray*sizeof(unsigned));
}

void
FramacVisitor::HandleTranslationUnit(clang::ASTContext &context)
{ int numberOfErrors = _commentHandler.compilerInstance().getDiagnostics()
      .getClient()->getNumErrors();
  if (numberOfErrors > 0) {
    if (numberOfErrors == 1)
      std::cerr << "code generation aborted due to one compilation error";
    else
      std::cerr << "code generation aborted due to compilation errors";
    std::cerr << std::endl;
    exit(2);
  };
  _context = &context;
  _clangUtils->set_context(&context);
  _sema = &_commentHandler.compilerInstance().getSema();
  _parents.setTranlationUnit(context, *_sema);
  _state = SSynchronized;
  clang::SourceManager& sources = context.getSourceManager();
  auto fname = sources.getFileEntryForID(sources.getMainFileID())->getName();
  _intermediateAST->filename = fname.str().c_str();
  _lexicalContextForPostVisit = context.getTranslationUnitDecl();
  _semanticContextForPostVisit = context.getTranslationUnitDecl();
  TraverseDecl(context.getTranslationUnitDecl());
  handlePostVisit(context.getTranslationUnitDecl(),
    context.getTranslationUnitDecl());
  postHandleTranslationUnit();
  _lexicalContextForPostVisit = NULL;
  _semanticContextForPostVisit = NULL;
  bool haveWaitingDeclarationsBeenGenerated
    = _tableForWaitingDeclarations.isComplete();
  if (!_alternateInstanceContexts.empty()) {
     std::cerr << "Internal error in the visit of RecordDecl "
        "and friend declarations\n";
#if 1
     assert(false);
#endif
  };
  if (!haveWaitingDeclarationsBeenGenerated
      || _delayedImplementations.getFront()
      || !_immediateGlobalDependency.isEmpty()) {
    assert(_immediateGlobalDependency.isEmpty());
    if (!haveWaitingDeclarationsBeenGenerated) {
      std::cerr << "The following declarations have not been generated:\n";
      _tableForWaitingDeclarations.printIncompleteDeclarations(std::cerr);
    }
    else if (_delayedImplementations.getFront()) {
      auto current =
        ((translation_unit_decl)(*_delayedImplementations.getFront())
         .element.container)
        ->cons_translation_unit_decl.Function.fun_name
        ->cons_decl_or_impl_name.Implementation.name;
      std::cerr << "Assertion broken in:" <<
        current->decl_name << std::endl;
      std::cerr << "\n" ;
    }
#if 1
    assert(!haveWaitingDeclarationsBeenGenerated
      ? (bool) _delayedImplementations.getFront()
      : !_delayedImplementations.getFront());
#endif
  };
  assert(!_rttiTable.hasDelayedMethodCalls());
  assert(!_clangUtils->hasTemplateInstance());
  outputIntermediateFormat();
}

// [TODO] see the implementation of
//   RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const
//   for the header of ::Visitor::Visit... methods handling with comments.

// [TODO] see the implementation of
//   const RawComment *ASTContext::getRawCommentForAnyRedecl(
//       const Decl *D, const Decl **OriginalDecl) const
//   for the header of ::Visitor::Visit... methods handling with comments.

init_expr
FramacVisitor::makeInitExpr(
  const clang::QualType & typ, const clang::Expr* init, bool* shouldDelay) {
  if (init->getStmtClass() == clang::Stmt::ExprWithCleanupsClass) {
    const auto expr = llvm::dyn_cast<const clang::ExprWithCleanups>(init);
    // it might mask an InitListExpr. ExprWithCleanups does not have
    // reference to destructors to call at the end of the block (and more
    // importantly when to call them) and Frama-C 15 Silicium will be equipped
    // to deal with them anyways: nothing is really needed on the C++ side
    // of the plugin.
    return makeInitExpr(typ, expr->getSubExpr(), shouldDelay);
  }
  if (init->getStmtClass() == clang::Stmt::InitListExprClass) {
    assert (llvm::dyn_cast<const clang::InitListExpr>(init));
    clang::InitListExpr const *init_list
        = static_cast<const clang::InitListExpr*>(init);
    // Use the semantic form, where compound initialization is done
    // in appropriate order.
    if (!init_list->isSemanticForm() && init_list->getSemanticForm()) {
      init_list = init_list->getSemanticForm();
    }
    const clang::FieldDecl* union_field
      = init_list->getInitializedFieldInUnion();
    if (union_field) {
      const char* field_name = copy_string(union_field->getNameAsString());
      clang::QualType ctype = union_field->getType();
      qual_type field_type = makeDefaultType(union_field->getLocation(), ctype);
      clang::InitListExpr::const_iterator field_init = init_list->begin();
      clang::InitListExpr::const_iterator field_end = init_list->end();
      if (field_init+1 != field_end) {
        std::cerr << "More than one initializer for an union.\n";
#if CLANG_VERSION_MAJOR >= 11
        init->dump(llvm::errs(), *_context);
#else
        init->dump(llvm::errs(), _context->getSourceManager());
#endif
        std::cerr << "Aborting\n";
        exit(2);
      };
      init_expr definition = makeInitExpr(
          ctype, static_cast<clang::Expr const *>(*field_init), shouldDelay);
      return init_expr_Union_init(field_name,field_type,definition);
    }
    clang::InitListExpr::const_reverse_iterator current = init_list->rbegin();
    clang::InitListExpr::const_reverse_iterator end = init_list->rend();
    list /* init_expr */ inits = NULL;
    if (_implicitThisStar) {
      free_expression(_implicitThisStar);
      _implicitThisStar = NULL; // Compound initialization is a C thing
      _isImplicitThisBare = false;
    };
    while(current != end) {
      auto subinit = static_cast<const clang::Expr*>(*current);
      init_expr curr_init =
        makeInitExpr(subinit->getType(), subinit, shouldDelay);
      inits = cons_container(curr_init,inits);
      current++;
    }
    return init_expr_Compound_init(inits);
  };
  if (typ->isConstantArrayType() &&
      init->getStmtClass() == clang::Stmt::CXXConstructExprClass) {
    // array of objects initialized with default constructor.
    assert(_implicitThisStar);
    qualified_name idx = qualified_name_cons(NULL, "__fc_idx");
    _implicitThisStar =
      expression_cons(
        copy_loc(_implicitThisStar->eloc),
        exp_node_ArrayAccess(
          _implicitThisStar,
          expression_cons(
            copy_loc(_implicitThisStar->eloc),
            exp_node_Variable(variable_Local(idx)))));
    return
      init_expr_Array_init(
        qualified_name_dup(idx), makeLocExpression(init,shouldDelay));
  };
  if (clang::ImplicitValueInitExpr::classof(init)) {
    return init_expr_Implicit_init();
  }
  //TODO: there are other initializer classes.
  //Need to check whether they are used in the elaborated AST.
  // plain expression
   return init_expr_Single_init(makeLocExpression(init, shouldDelay));
}

init_expr FramacVisitor::make_explicit_initializer_list(const clang::Expr* e) {
  // It's likely that we'll always end up with a MaterializeTemporaryExpr,
  // but there might be cases where clang give the list directly in e.
  if (e->getStmtClass() == clang::Stmt::MaterializeTemporaryExprClass) {
    auto tmp = llvm::dyn_cast<const clang::MaterializeTemporaryExpr>(e);
#if CLANG_VERSION_MAJOR >= 10
    e = tmp->getSubExpr();
#else
    e = tmp->GetTemporaryExpr();
#endif
  }
  assert("Expecting an InitListExpr under a CXXStdInitializeListExpr"
         && e->getStmtClass() == clang::Stmt::InitListExprClass);
  auto list = llvm::dyn_cast<const clang::InitListExpr>(e);
  return makeInitExpr(list->getType(), list);
}

exp_node FramacVisitor::make_initializer_list(
  const clang::CXXStdInitializerListExpr* il) {
  const clang::QualType typ = il->getType();
  const clang::CXXRecordDecl* il_type = typ->getAsCXXRecordDecl();
  assert(il_type);
  auto il_inst =
    llvm::dyn_cast<const clang::ClassTemplateSpecializationDecl>(il_type);
  assert(il_inst);
  const clang::QualType elt_type =
    il_inst->getTemplateInstantiationArgs().get(0).getAsType();
  qual_type elt_type_trans =
    makeDefaultType(_clangUtils->getBeginLoc(*il), elt_type);
  init_expr init = make_explicit_initializer_list(il->getSubExpr());
  return exp_node_InitializerList(elt_type_trans, init);
}

exp_node FramacVisitor::make_lambda_expr(const clang::LambdaExpr* lam) {
  const clang::CXXMethodDecl* lam_meth = lam->getCallOperator();
  qual_type lam_rt =
    makeDefaultExternalNameType(
      lam_meth->getReturnTypeSourceRange().getBegin(),
      lam_meth->getReturnType());
  /* arg_decl */ list lam_args = NULL;
  auto args = lam_meth->parameters();
  for (auto it = args.rbegin(); it < args.rend(); it++) {
    std::string name = (*it)->getNameAsString();
    qual_type arg_type =
      makeDefaultExternalNameType(
        (*it)->getLocation(), (*it)->getOriginalType());
    location l = makeLocation((*it)->getSourceRange());
    lam_args =
      cons_container(arg_decl_cons(arg_type, copy_string(name), l),lam_args);
  }
  /* closure */ list lam_closure =
    _clangUtils->make_capture_list(lam->captures());
  /* statement */ list lam_body =
    makeCodeBlock(lam_meth->getBody(), lam_meth->getDeclContext(), lam_meth);
  return exp_node_LambdaExpr(lam_rt, lam_args, lam_closure, lam_body);
}

expression
FramacVisitor::makeLocExpression(
  const clang::Expr* expr, bool* shouldDelay, list* receiver) {
  exp_node node = makeExpression(expr, shouldDelay, receiver);
  location loc = makeLocation(expr->getSourceRange());
  return expression_cons(loc,node);
}

exp_node FramacVisitor::convert_result(
  FramaCIRGenAction::ExpressionGuard& guard, const clang::QualType& ret,
  exp_node call, const clang::Expr* expr) {
  exp_node result = call;
  if (ret->isReferenceType())
    result =
      exp_node_Dereference(
        expression_cons(makeLocation(expr->getSourceRange()), result));
  clang::CXXRecordDecl* class_decl = ret->getAsCXXRecordDecl();
  if (class_decl) {
    clang::CXXConstructorDecl* cons =
      _sema->LookupCopyingConstructor(
        class_decl,ret.getCVRQualifiers());
    if(!cons) {
      std::cerr << 
        "Not able to find an appropriate copy constructor for class "
                << class_decl->getNameAsString() << "\nAborting.\n";
      exit(2);
    }
    // create a temporary in case the address is taken
    // but do not use copy constructor, as clang has determined
    // that it can be elided (§12.8.32).
    const char* temp = mk_tmp_name ();
    qual_type typ =makeDefaultType(expr->getExprLoc(), ret);
    expression res =
      expression_cons(makeLocation(expr->getSourceRange()),result);
    result = exp_node_Temporary(temp,typ,init_expr_Single_init(res), false);
  }
  return guard.setAssignResult(result, expr);
}

exp_node FramacVisitor::makeTemporaryObjectExpression(
  const clang::CXXTemporaryObjectExpr* constructor,
  bool* shouldDelay, /* statement */ list* receiver)
{ clang::SourceLocation sloc = constructor->getExprLoc();
  location loc = makeLocation(constructor->getSourceRange());
  assert(llvm::dyn_cast<clang::FunctionDecl>(constructor->getConstructor()));
  const clang::FunctionDecl* function = static_cast<const clang
      ::FunctionDecl*>(constructor->getConstructor());
  tkind templateExtension = NULL;
  if (function->getTemplateSpecializationKind()
      >= clang::TSK_ImplicitInstantiation) {
    clang::FunctionTemplateSpecializationInfo* info
      = function->getTemplateSpecializationInfo();
    if (info)
      templateExtension=
        tkind_TTemplateInstance(_clangUtils->getTemplateExtension(info));
  };
  if (!templateExtension)
    templateExtension = tkind_TStandard();
  qualified_name name = _clangUtils->makeQualifiedName(*function);
  if (strlen(name->decl_name) == 0) {
    free(const_cast<char*>(name->decl_name));
    name->decl_name = strdup("constructor-special");
  };
  clang::CXXConstructExpr::const_arg_iterator argEnd = constructor->arg_end();
  list /* expression */ arguments = NULL;
  ForwardReferenceList forwardArguments(arguments);
  const char* temp = mk_tmp_name();
  // [TODO] declare tmpvar in the list of local variables ?
  expression tmpvar = expression_cons(loc, exp_node_Address(
      expression_cons(copy_loc(loc), exp_node_Variable(
        variable_Local(qualified_name_cons(NULL,temp))))));
  forwardArguments.insertContainer(tmpvar);
  for (clang::CXXConstructExpr::const_arg_iterator
      argIter = constructor->arg_begin(); argIter != argEnd; ++argIter) {
    exp_node argument = makeExpression(*argIter, shouldDelay,receiver);
    expression eargument = expression_cons(makeLocation(
          (*argIter)->getSourceRange()), argument);
    forwardArguments.insertContainer(eargument);
  };
  signature sig = _clangUtils->makeSignature(*constructor->getConstructor());
  exp_node call_node =
    exp_node_Static_call(
      name, sig, funkind_FKConstructor(true),
      arguments, templateExtension, false);
  expression call = expression_cons(copy_loc(loc),call_node);
  qual_type typ = makeDefaultType(sloc, constructor->getType());
  return exp_node_Temporary(strdup(temp), typ,
      init_expr_Single_init(call), false);
}

exp_node FramacVisitor::makeConstructExpression(
  const clang::CXXConstructExpr* constructor,
  bool* shouldDelay, /* statement */ list* receiver,
  bool hasImplicitThisStar,
  bool isImplicitThisBare,
  FramaCIRGenAction::ExpressionGuard& guard)
{ tkind templateExtension = NULL;
  if (constructor->isElidable() && constructor->getNumArgs() == 1) {
    // constructor can be omitted. Gain quite a few temporary variables
    // by doing it.
    // TODO: maybe some additional checks are needed before doing the elision.
    return makeExpression(constructor->getArg(0), shouldDelay, receiver);
  }
  assert(llvm::dyn_cast<clang::CXXMethodDecl>(constructor->getConstructor()));
  const clang::CXXMethodDecl* function = static_cast<const clang
      ::CXXMethodDecl*>(constructor->getConstructor());
  if (function->getParent()->isLambda() && constructor->getNumArgs() == 1) {
    // Some Lambda constructors are not seen as elidable. Nevertheless, we
    // use a custom translation mechanism there and bypass the constructor.
    return makeExpression(constructor->getArg(0), shouldDelay, receiver);
  }
  if (function->getTemplateSpecializationKind()
      >= clang::TSK_ImplicitInstantiation) {
    clang::FunctionTemplateSpecializationInfo* info
      = function->getTemplateSpecializationInfo();
    if (info)
      templateExtension=tkind_TTemplateInstance(
        _clangUtils->getTemplateExtension(info));
  };
  if (!templateExtension)
    templateExtension = tkind_TStandard();
  if (hasInstanceContext()) {
    if (!_tableForWaitingDeclarations.hasVisited(function)
         && function != _parents.getSFunctionDecl())
      unvisitedDecls().push_back(function);
    else {
      clang::Decl::Kind kindDecl = function->getDeclContext()->getDeclKind();
      if (kindDecl >= clang::Decl::firstRecord
          && kindDecl <= clang::Decl::lastRecord)
        _tableForWaitingDeclarations.ensureGeneration(
          static_cast<const clang::RecordDecl*>(function->getDeclContext()),
          unvisitedDecls());
    };
  }
  else if (shouldDelay && !*shouldDelay)
    *shouldDelay = !_tableForWaitingDeclarations.hasVisited(function)
         && function != _parents.getSFunctionDecl();

  qualified_name name = _clangUtils->makeQualifiedName(*function);
  if (strlen(name->decl_name) == 0) {
    free(const_cast<char*>(name->decl_name));
    name->decl_name = strdup("constructor-special");
  };
  if (isImplicitThisBare) {
    _rttiTable.addBareToQualification(name);
    // if (shouldDelay && !*shouldDelay)
    //   *shouldDelay = true;
  };
  clang::CXXConstructExpr::const_arg_iterator argEnd = constructor->arg_end();
  list /* expression */ arguments = NULL;
  ForwardReferenceList forwardArguments(arguments);
  clang::SourceLocation sloc = constructor->getExprLoc();
  location loc = makeLocation(constructor->getSourceRange());
  const char* temp = NULL;
  expression tmpvar;
  qual_type typ;
  if (hasImplicitThisStar) {
    expression lval = guard.releaseExpr ();
    expression address = expression_cons(loc,exp_node_Address(lval));
    tmpvar = address;
  }
  else {
    temp = mk_tmp_name();
    tmpvar = expression_cons(loc, exp_node_Address(
        expression_cons(copy_loc(loc), exp_node_Variable(
          variable_Local(qualified_name_cons(NULL,temp))))));
    typ = makeDefaultType(sloc, constructor->getType());
  };
  forwardArguments.insertContainer(tmpvar);
  for (clang::CXXConstructExpr::const_arg_iterator
      argIter = constructor->arg_begin(); argIter != argEnd; ++argIter) {
    exp_node argument = makeExpression(*argIter, shouldDelay,receiver);
    expression eargument = expression_cons(makeLocation(
          (*argIter)->getSourceRange()), argument);
    forwardArguments.insertContainer(eargument);
  };
  signature sig = _clangUtils->makeSignature(*constructor->getConstructor());
  exp_node call_node =
    exp_node_Static_call(
      name, sig, funkind_FKConstructor(true),
      arguments, templateExtension, false);
  expression call = expression_cons(copy_loc(loc),call_node);
  exp_node res = temp ? exp_node_Temporary(strdup(temp), typ,
      init_expr_Single_init(call), false) : call_node;
  return res;
}

exp_node FramacVisitor::makeDeleteExpression(
    const clang::CXXDeleteExpr* deleteExpr,
    bool* shouldDelay, /* statement */ list* receiver) {
  location loc = makeLocation(deleteExpr->getSourceRange());
  expression thisArgument =
    makeLocExpression(deleteExpr->getArgument(), shouldDelay, receiver);
  const clang::CXXRecordDecl* recordDecl =
    deleteExpr->getDestroyedType()->getAsCXXRecordDecl();
  /* statement */ list statements = NULL;

  if (deleteExpr->getOperatorDelete()
      && !deleteExpr->getOperatorDelete()->isImplicit()) {
    /* overloaded delete */
    const clang::FunctionDecl* function = deleteExpr->getOperatorDelete();
    if (hasInstanceContext()) {
      if (!_tableForWaitingDeclarations.hasVisited(function)
            && function != _parents.getSFunctionDecl())
        unvisitedDecls().push_back(function);
      else {
        clang::Decl::Kind kindDecl = function->getDeclContext()->getDeclKind();
        if (kindDecl >= clang::Decl::firstRecord
            && kindDecl <= clang::Decl::lastRecord)
          _tableForWaitingDeclarations.ensureGeneration(
            static_cast<const clang::RecordDecl*>(function->getDeclContext()),
            unvisitedDecls());
      };
    }
    else if (shouldDelay && !*shouldDelay)
      *shouldDelay = !_tableForWaitingDeclarations.hasVisited(function)
            && function != _parents.getSFunctionDecl();
    qualified_name name = _clangUtils->makeQualifiedName(
        *deleteExpr->getOperatorDelete());
    list /* expression */ arguments = cons_container(thisArgument, NULL);
    signature sig =
      _clangUtils->makeSignature(*deleteExpr->getOperatorDelete());
    // Call of overloaded delete
    statements =
      cons_container(
        statement_Expression(
          copy_loc(loc),
          expression_cons(
            copy_loc(loc),
            exp_node_Static_call(
              name,
              sig,
              funkind_FKFunction(),
              arguments,
              tkind_TStandard(),
              false))),
        statements);
  }
  else if (!deleteExpr->isArrayForm()) { // ::delete
    // Free
    statements =
      cons_container(
        statement_Expression(
          copy_loc(loc),
          expression_cons(
            copy_loc(loc),
            exp_node_Free(
              thisArgument))),
        statements);
  }
  else { // ::delete[]
    // Free for array
    statements =
      cons_container(
        statement_Expression(
          copy_loc(loc),
          expression_cons(
            copy_loc(loc),
            exp_node_FreeArray(
              thisArgument))),
        statements);
  }

  if(recordDecl) {
    clang::CXXDestructorDecl* destructor = recordDecl->getDestructor();
    if(destructor) {
      if (hasInstanceContext()) {
        if (!_tableForWaitingDeclarations.hasVisited(destructor)
               && destructor != _parents.getSFunctionDecl())
          unvisitedDecls().push_back(destructor);
        else {
          clang::Decl::Kind kindDecl = destructor->getDeclContext()
              ->getDeclKind();
          if (kindDecl >= clang::Decl::firstRecord
              && kindDecl <= clang::Decl::lastRecord) {
             _tableForWaitingDeclarations.ensureGeneration(
              static_cast<const clang::RecordDecl*>(
                destructor->getDeclContext()),
              unvisitedDecls());
            }
        };
      }
      else if (shouldDelay && !*shouldDelay) {
        *shouldDelay = !_tableForWaitingDeclarations.hasVisited(destructor)
            && destructor != _parents.getSFunctionDecl();
      }
      qualified_name name = _clangUtils->makeQualifiedName(*destructor);

      if (deleteExpr->isArrayForm()) {
        // "i"
        const char* loopVariableName = mk_tmp_name();
        // i
        expression loopVariableAsExpr =
          expression_cons(
            copy_loc(loc),
            exp_node_Variable(
              variable_Local(
                qualified_name_cons(
                  NULL,
                  loopVariableName))));
        expression zero = makeIntLiteral(loc,IULONGLONG,0);
        expression one = makeIntLiteral(loc,IULONGLONG,1);
        // length. As said above, should be dependent on the argument of free[]
        expression size = makeIntLiteral(loc,IULONGLONG,1);
        // n-1-i
        expression index =
          expression_cons(
            copy_loc(loc),
            exp_node_Binary_operator(
              BOMINUS,
              AKRVALUE,
              expression_cons(
                copy_loc(loc),
                exp_node_Binary_operator(
                  BOMINUS,
                  AKRVALUE,
                  size,
                  one)),
            loopVariableAsExpr));
        // p[n-1-i]
        expression nthElt =
          expression_cons(
            copy_loc(loc),
            exp_node_ArrayAccess(thisArgument, index));
        // TODO: verify if the following instruction is still needed
        // expression thisCopyArgument =
        //   makeLocExpression(deleteExpr->getArgument(),shouldDelay, receiver);
        // TODO : create a temp expression to avoid side effects
        list /* expression */ arguments =
          cons_container(
            expression_cons(
              copy_loc(loc),
              exp_node_Address(
                nthElt)),
            NULL);
        signature sig = _clangUtils->makeSignature(*destructor);
        // A::Dtor(&p[n-1-i])
        exp_node call_node =
          exp_node_Static_call(
            name,
            sig,
            funkind_FKDestructor(true),
            arguments,
            tkind_TStandard(),
            false);
        expression call = expression_cons(copy_loc(loc),call_node);
        // unsigned long long
        qual_type loopVariableType =
          qual_type_cons(
            NULL,
            typ_Int(
              IULONGLONG));
        // (unsigned long long)0;
        init_expr loopVariableInit =
          init_expr_Single_init(
            zero);
        // unsigned long long i = 0;
        list init =
          cons_container(
            init_declarator_cons(
              loopVariableName,
              loopVariableType,
              opt_some_container(
                loopVariableInit)),
            NULL);
        // ++i
        incr_statement incr =
          incr_statement_CExpression(
            expression_cons(
              copy_loc(loc),
              exp_node_Unary_operator(
                uokind_UOPreInc(),
                loopVariableAsExpr)));
        // i < length
        expression cond =
          expression_cons(
            copy_loc(loc),
            exp_node_Binary_operator(
              BOLESS,
              AKRVALUE,
              loopVariableAsExpr,
              size));
        // for(unsigned long long i = 0; i < length; ++i)
        //   A::Dtor(&p[n-1-i]);
        statement forStatement =
          statement_For(
            copy_loc(loc),
            init_statement_IVarDecl(
              init),
            opt_some_container(
              cond),
            incr,
            statement_Expression(
              copy_loc(loc),
              call),
            NULL);
        statements = cons_container(forStatement, statements);
      }
      else { //non array
        expression thisCopyArgument =
          makeLocExpression(deleteExpr->getArgument(), shouldDelay, receiver);
        // TODO : create a temp expression to avoid side effects
        list /* expression */ arguments =
          cons_container(thisCopyArgument, NULL);
        signature sig = _clangUtils->makeSignature(*destructor);
        exp_node call_node =
          exp_node_Static_call(
            name,
            sig,
            funkind_FKDestructor(true),
            arguments,
            tkind_TStandard(),
            false);
        statements =
          cons_container(
            statement_Expression(
              copy_loc(loc),
              expression_cons(
                copy_loc(loc),
                call_node)),
            statements);
      }
    }
  };
  return exp_node_GnuBody(statements);
}

exp_node FramacVisitor::makeNewExpression(
  const clang::CXXNewExpr* newExpr,
  bool* shouldDelay, /* statement */ list* receiver) {
  exp_node result = NULL;
  qual_type type =
    makeDefaultType(newExpr->getExprLoc(), newExpr->getAllocatedType());

  // First, we compute the address of the memory block where the object will
  // be stored. Various cases are possible, depending on the nature of
  // operator new used.

  // TODO: find a way to distinguish default placement new from custom one
  if (newExpr->getOperatorNew() && 
      !newExpr->getOperatorNew()->isReplaceableGlobalAllocationFunction() &&
      !newExpr->getOperatorNew()->isReservedGlobalPlacementOperator()) {
    // custom placement new. Just make a call to the corresponding function.
    qualified_name name
        = _clangUtils->makeQualifiedName(*newExpr->getOperatorNew());
    signature sig = _clangUtils->makeSignature(*newExpr->getOperatorNew());
    /* expression */ list arguments = NULL;
    ForwardReferenceList argumentsList(arguments);
    location loc = makeLocation(newExpr->getSourceRange());
    exp_node size_to_alloc =
      exp_node_Constant(
        compilation_constant_TypeCst(TCCSIZEOF,type->plain_type));
    if (newExpr->isArray()) {
      expression exp_size_elt = expression_cons(copy_loc(loc), size_to_alloc);
      // length
      expression exp_length;
#if CLANG_VERSION_MAJOR >= 9
      if (!newExpr->getArraySize().hasValue())
        exp_length = makeIntLiteral(copy_loc(loc), IINT, 0);
      else
        exp_length =
          makeLocExpression(*newExpr->getArraySize(), shouldDelay, receiver);
#else
      exp_length =
        makeLocExpression(newExpr->getArraySize(), shouldDelay, receiver);
#endif

      // size = length * sizeof(A)
      size_to_alloc =
        exp_node_Binary_operator(BOTIMES, AKRVALUE, exp_length, exp_size_elt);
    };
    argumentsList.insertContainer(expression_cons(copy_loc(loc),size_to_alloc));
    for (unsigned i = 0; i < newExpr->getNumPlacementArgs(); i++) {
      expression arg =
        makeLocExpression(newExpr->getPlacementArg(i), shouldDelay, receiver);
      argumentsList.insertContainer(arg);
    }
    // Call the explicit new operator
    // new(sizeof(A))
    result = exp_node_Static_call(
        name, sig, funkind_FKFunction(), arguments, tkind_TStandard(), false);
  } else if (newExpr->getNumPlacementArgs()) {
      // implicit placement new: just reuse the provided memory block
      // implicit placement new has exactly one argument, the address
      // of the memory block where the object should be put
      assert(newExpr->getNumPlacementArgs() == 1);
      result =
        makeExpression(newExpr->getPlacementArg(0), shouldDelay, receiver);
  } else if (!newExpr->isArray()) { // no placement, no array
      // malloc(sizeof(A))
      result = exp_node_Malloc(type->plain_type);
  } else {
#if CLANG_VERSION_MAJOR >= 9
      expression size;
      if (!newExpr->getArraySize().hasValue())
        size = makeIntLiteral(makeLocation(newExpr->getSourceRange()), IINT, 0);
      else
        size =
          makeLocExpression(*newExpr->getArraySize(), shouldDelay, receiver);
#else
      expression size =
        makeLocExpression(newExpr->getArraySize(), shouldDelay, receiver);
#endif
      // malloc(length * sizeof(A))
      result = exp_node_MallocArray(type->plain_type, size);
  }
  expression eresult =
    expression_cons(makeLocation(newExpr->getSourceRange()), result);
  type->plain_type = NULL;
  free_type(type);

  // Now, we have to initialize the content of the memory.
  if (newExpr->hasInitializer()) {
    const clang::CXXConstructExpr* constructor = newExpr->getConstructExpr();
    // Save the pointer returned by malloc in a fresh temporary variable
    const char* temp = mk_tmp_name();
    location loc = makeLocation(newExpr->getSourceRange());
    expression tmp_expr =
      expression_cons(
        copy_loc(loc),
        exp_node_Variable(variable_Local(qualified_name_cons(NULL, temp))));
    expression tmpvar =
      expression_cons(loc, exp_node_Assign(tmp_expr, eresult));
    if (!constructor) {
      // Initializing a scalar value, as in new int (10);
      // Translate that as a local definition + an assignment:
      // (int * tmp = malloc(sizeof(int)), *tmp = 10, tmp);
      result =
        exp_node_Temporary(
          strdup(temp),
          qual_type_cons(
            NULL,
            typ_Pointer(
              pkind_PDataPointer(
                makeDefaultType(
                  newExpr->getExprLoc(), newExpr->getAllocatedType())))),
          init_expr_Single_init(eresult), false);
      eresult = expression_cons(copy_loc(loc), result);
      expression init =
        makeLocExpression(newExpr->getInitializer(), shouldDelay, receiver);
      expression assign =
        expression_cons(
          copy_loc(loc),
          exp_node_Assign(
            expression_cons(
              copy_loc(loc),
              exp_node_Dereference(tmp_expr)),init));
      result = exp_node_Binary_operator(BOCOMMA, AKRVALUE, eresult, assign);
      eresult = expression_cons(copy_loc(loc), result);
      result =
        exp_node_Binary_operator(
          BOCOMMA, AKRVALUE, eresult, expression_dup(tmp_expr));
    }
    else {
      if(!newExpr->isArray()) {
          // *(tmp = (A*)malloc()) = initializer
        eresult =
          expression_cons(
            loc,
            exp_node_Dereference(
              expression_cons(
                copy_loc(loc),
                exp_node_PointerCast(
                  qual_type_cons(
                    NULL,
                    typ_LVReference(
                      pkind_PDataPointer(
                        makeDefaultType(
                          newExpr->getExprLoc(),
                          newExpr->getAllocatedType())))),
              reference_or_pointer_kind_RPKPointer(),
              tmpvar))));
        FramaCIRGenAction::ExpressionGuard guard(*this, eresult);
        // A::Ctor(...)
        exp_node call_node = makeConstructExpression(constructor, shouldDelay,
          receiver, true, false, guard);
        result =
          exp_node_Temporary(
            strdup(temp),
            qual_type_cons(
              NULL,
              typ_Pointer(
                pkind_PDataPointer(
                  makeDefaultType(
                    newExpr->getExprLoc(), newExpr->getAllocatedType())))),
            init_expr_Single_init(
              expression_cons(
                copy_loc(loc),
                call_node)),
            false);
      }
      else {
        // tmp = malloc()
        statement allocation = statement_Expression(copy_loc(loc), tmpvar);
        // "i"
        const char* loopVariableName = mk_tmp_name();
        // i
        expression loopVariableAsExpr =
          expression_cons(
            copy_loc(loc),
            exp_node_Variable(
              variable_Local(qualified_name_cons(NULL, loopVariableName))));
        // tmp[i]
        expression nthElt =
          expression_cons(
            copy_loc(loc),
            exp_node_ArrayAccess(expression_dup(tmp_expr), loopVariableAsExpr));
        FramaCIRGenAction::ExpressionGuard guard(*this, nthElt);
        // A::Ctor(&tmp[i])
        exp_node call_node = makeConstructExpression(constructor, shouldDelay,
          receiver, true, false, guard);
        expression call = expression_cons(copy_loc(loc),call_node);
        // unsigned long long
        qual_type loopVariableType =
          qual_type_cons(
            NULL,
            typ_Int(
              IULONGLONG));
        // (unsigned long long)0;
        init_expr loopVariableInit =
          init_expr_Single_init(makeIntLiteral(loc,IULONGLONG,0));
        // unsigned long long i = 0;
        list init =
          cons_container(
            init_declarator_cons(
              loopVariableName,
              loopVariableType,
              opt_some_container(
                loopVariableInit)),
            NULL);
        // ++i
        incr_statement incr =
          incr_statement_CExpression(
            expression_cons(
              copy_loc(loc),
              exp_node_Unary_operator(
                uokind_UOPreInc(),
                loopVariableAsExpr)));
        // length
        expression size =
#if CLANG_VERSION_MAJOR >= 9
          (!newExpr->getArraySize().hasValue())
            ? makeIntLiteral(copy_loc(loc), IINT, 0)
            : makeLocExpression(
                *newExpr->getArraySize(),
                shouldDelay,
                receiver);
#else
          expression_cons(
            copy_loc(loc),
            makeExpression(
              newExpr->getArraySize(),
              shouldDelay,
              receiver));
#endif
        // i < length
        expression cond =
          expression_cons(
            copy_loc(loc),
            exp_node_Binary_operator(
              BOLESS,
              AKRVALUE,
              loopVariableAsExpr,
              size));
        // for(unsigned long long i = 0; i < length; ++i)
        //   A::Ctor(&tmp[i]);
        statement forStatement =
          statement_For(
            copy_loc(loc),
            init_statement_IVarDecl(
              init),
            opt_some_container(
              cond),
            incr,
            statement_Expression(
              copy_loc(loc),
              call),
            NULL);
        list statements = NULL;
        statements =
          cons_container(
            statement_Expression(
              copy_loc(loc),
              expression_cons(
                copy_loc(loc),
                exp_node_Variable(
                  variable_Local(
                    qualified_name_cons(
                      NULL,
                      temp))))),
            statements);
        statements = cons_container(forStatement, statements);
        statements = cons_container(allocation, statements);
        statements =
          cons_container(
            statement_VarDecl(
              copy_loc(loc),
              strdup(temp),
              qual_type_cons(
                NULL,
                typ_Pointer(
                  pkind_PDataPointer(
                    makeDefaultType(
                      newExpr->getExprLoc(),
                      newExpr->getAllocatedType())))),
              opt_none()),
            statements);
        /*
         * statements:
         * A* tmp = malloc(length * sizeof(A));
         * for(unsigned long long i = 0; i < length; ++i)
         *   A::Ctor(&tmp[i]);
         * tmp;
         */
        // ({statements})
        result = exp_node_GnuBody(statements);
      }
    };
  };
  return result;
}
void FramacVisitor::ensureBuiltinDeclaration(
  unsigned builtinID, const clang::FunctionDecl* fd, bool doesForceVariadic)
{ if (!isGeneratedBuiltin(builtinID)) {
    const char* name = copy_string(fd->getNameAsString ());
    clang::SourceLocation sloc = fd->getLocation();
    location loc = makeLocation(fd->getSourceRange());
    qual_type return_type =
      makeDefaultExternalNameType(sloc, fd->getReturnType());
    list /*arg_decl*/ prms = NULL;
    ForwardReferenceList params(prms);
    if (builtinID >= clang::Builtin::BI__atomic_load
        && builtinID <= clang::Builtin::BI__atomic_nand_fetch
        && return_type->plain_type->tag_typ == VOID) {
      free_typ(return_type->plain_type);
      return_type->plain_type = typ_Int(IINT);
      arg_decl cdecl = (arg_decl)malloc(sizeof(*cdecl));
      cdecl->arg_name = strdup("__frama_c_arg_0");
      cdecl->arg_type = qual_type_cons(NULL, typ_Pointer(
        pkind_PDataPointer(qual_type_cons(NULL, typ_Void()))));
      cdecl->arg_loc = copy_loc(loc);
      params.insertContainer(cdecl);
    }
    else if (builtinID == clang::Builtin::BI__builtin_va_end
          || builtinID == clang::Builtin::BI__builtin_va_start) {
      arg_decl cdecl = (arg_decl)malloc(sizeof(*cdecl));
      const clang::ParmVarDecl *prm = fd->getParamDecl(0);
      if (prm->getNameAsString().size() > 0)
        cdecl->arg_name = copy_string(prm->getNameAsString());
      else
        cdecl->arg_name = strdup("__frama_c_arg_0");
      cdecl->arg_type = qual_type_cons(NULL, typ_Named(
        qualified_name_cons(NULL, strdup("__builtin_va_list")), true));
      cdecl->arg_loc = makeLocation(prm->getSourceRange());
      params.insertContainer(cdecl);
    } else {
      int i = 0;
      clang::FunctionDecl::param_const_iterator stop = fd->param_end();
      for (clang::FunctionDecl::param_const_iterator it = fd->param_begin(); 
           it!=stop;it++) {
        if (it!=fd->param_begin() || !doesForceVariadic
            || !(*it)->getOriginalType()->isVoidType()) {
        std::string name = (*it)->getNameAsString();
        if (name.size() == 0) {
          std::ostringstream out;
          out << "__frama_c_arg_" << i++;
          name = out.str();
        };
        clang::SourceLocation sloc = (*it)->getLocation();
        qual_type prm_type =
          makeDefaultExternalNameType(sloc, (*it)->getOriginalType());
        location loc = makeLocation((*it)->getSourceRange());
        params.insertContainer(arg_decl_cons(prm_type,copy_string(name),loc));
        }
      }
    }
    decl_or_impl_name decl_name = decl_or_impl_name_Declaration(name);
    translation_unit_decl func =
      translation_unit_decl_Function(
        decl_name, funkind_FKFunction(), loc, return_type, prms,
        opt_none() /* body */, true /* fd->isExternC() */, false,
        doesForceVariadic || fd->isVariadic(), tkind_TStandard(),
        false /* has_further_definition */,
        opt_none() /* throws */,
        opt_none() /* contract */);
    if (_clangUtils->isExternCContext(_lexicalContextForPostVisit)
         && _clangUtils->isExternCContext(_semanticContextForPostVisit))
      _globals.insertContainer(func);
    else
      _globals.insertBeforeContainer(func);
    if (builtinID < clang::Builtin::FirstTSBuiltin)
      setGeneratedBuiltin(builtinID);
  };
}

bool FramacVisitor::is_lambda_call(const clang::CallExpr* call) {
  if (call->getStmtClass() == clang::Stmt::CXXOperatorCallExprClass) {
    const clang::CXXOperatorCallExpr* op =
      llvm::dyn_cast<clang::CXXOperatorCallExpr>(call);
    if (op->getOperator() == clang::OO_Call) {
      if (call->getNumArgs() > 0) {
        clang::QualType typ = call->getArg(0)->getType();
        const clang::CXXRecordDecl* rec = typ->getAsCXXRecordDecl();
        return (rec && rec->isLambda());
      }
    }
  }
  return false;
}

exp_node FramacVisitor::makeLambdaCallExpression(
  const clang::CallExpr* call, bool* shouldDelay, list* receiver,
  FramaCIRGenAction::ExpressionGuard& guard) {
  list arguments = NULL;
  clang::CallExpr::const_arg_iterator beg = call->arg_begin();
  clang::CallExpr::const_arg_iterator it = call->arg_end();
  expression lambda_obj = makeLocExpression(*beg, shouldDelay, receiver);
  beg++;
  while (beg < it) {
    it -= 1;
    expression arg = makeLocExpression(*it, shouldDelay, receiver);
    arguments = cons_container(arg, arguments);
  }
  exp_node result = exp_node_Lambda_call(lambda_obj, arguments);
  return
    convert_result(guard, call->getCallReturnType(*_context), result, call);
}

exp_node FramacVisitor::makeCallExpression(
  const clang::CallExpr* callExpr, bool* shouldDelay,
  /* statement */ list* receiver, FramaCIRGenAction::ExpressionGuard& guard) {
  funkind fkind;
  if (is_lambda_call(callExpr))
    return makeLambdaCallExpression(callExpr,shouldDelay,receiver,guard);
  const clang::FunctionDecl* fd = callExpr->getDirectCallee();
  if (fd) {
    clang::Decl::Kind kind = fd->getDeclKind();
    if (kind == clang::Decl::CXXConstructor)
      fkind = funkind_FKConstructor(true);
    else if (kind == clang::Decl::CXXDestructor)
      fkind = funkind_FKDestructor(true);
    else if ((kind >= clang::Decl::firstCXXMethod)
        && (kind <= clang::Decl::lastCXXMethod)) {
      if (fd->getCanonicalDecl()->getStorageClass() != clang::SC_Static)
        fkind = _clangUtils->cv_meth(
          static_cast<const clang::CXXMethodDecl*>(fd));
      else
        fkind = funkind_FKFunction();
    }
    else
      fkind = funkind_FKFunction();
    unsigned builtinID = fd->getBuiltinID();
    if (builtinID > 0) {
      ensureBuiltinDeclaration(builtinID, fd);
      _tableForWaitingDeclarations.addDeclaration(fd,_globals);
      _instanceContexts.removeUnvisited(fd);
    }
  }
  else if (clang::CXXOperatorCallExpr::classof(callExpr))
    /* TODO: check whether an operator can have cv qualification */
    fkind = funkind_FKMethod(NULL);
  else fkind = funkind_FKFunction ();
  clang::CallExpr::const_arg_iterator argIterEnd = callExpr->arg_end();
  list /* expression */ arguments = NULL;
  ForwardReferenceList forwardArguments(arguments);
  bool isFirstObject = (fkind->tag_funkind != FKFUNCTION);
  for (clang::CallExpr::const_arg_iterator argIter=callExpr->arg_begin();
      argIter != argIterEnd; ++argIter) {
    expression arg = makeLocExpression(*argIter, shouldDelay,receiver);
    if (isFirstObject) {
      arg =
        expression_cons(
          makeLocation((*argIter)->getSourceRange()),
          exp_node_Address(arg));
    };
    forwardArguments.insertContainer(arg);
    isFirstObject = false;
  };
  const clang::FunctionDecl* directCall = callExpr->getDirectCallee();
  if (directCall) {
    tkind templateExtension = NULL;
    if (directCall->getTemplateSpecializationKind()
        >= clang::TSK_ImplicitInstantiation) {
      clang::FunctionTemplateSpecializationInfo* info
          = directCall->getTemplateSpecializationInfo();
      if (info)
        templateExtension =
          tkind_TTemplateInstance(_clangUtils->getTemplateExtension(info));
    };
    if (!templateExtension)
      templateExtension = tkind_TStandard();
    qualified_name name = _clangUtils->makeQualifiedName(*directCall);
    if (fkind->tag_funkind == FKCONSTRUCTOR && strlen(name->decl_name) == 0) {
      free(const_cast<char*>(name->decl_name));
      name->decl_name = strdup("constructor-special");
    };
    if (hasInstanceContext()) {
      if (!_tableForWaitingDeclarations.hasVisited(directCall)
            && directCall != _parents.getSFunctionDecl())
        unvisitedDecls().push_back(directCall);
      else {
        clang::Decl::Kind kindDecl = directCall->getDeclContext()->getDeclKind();
        if (kindDecl >= clang::Decl::firstRecord
            && kindDecl <= clang::Decl::lastRecord)
          _tableForWaitingDeclarations.ensureGeneration(
            static_cast<const clang::RecordDecl*>(directCall->getDeclContext()),
            unvisitedDecls());
      };
    }
    else if (shouldDelay && !*shouldDelay)
      *shouldDelay = !_tableForWaitingDeclarations.hasVisited(directCall)
            && directCall != _parents.getSFunctionDecl();

    signature sig = _clangUtils->makeSignature(*directCall);
    exp_node result =
      exp_node_Static_call(
        name, sig, fkind, arguments,
        templateExtension, directCall->isInExternCContext());
    return
      convert_result(
        guard,callExpr->getCallReturnType(*_context),result,callExpr);
  };
  if (callExpr->getCallee()->getStmtClass()
      == clang::Stmt::CXXPseudoDestructorExprClass)
    return exp_node_Constant(compilation_constant_IntCst(IINT,
            ICSTATICCONST, 0));
  expression call =
    makeLocExpression(callExpr->getCallee(), shouldDelay,receiver);
  return
    convert_result(
      guard,
      callExpr->getCallReturnType(*_context),
      exp_node_Dynamic_call(fkind, call, arguments),
      callExpr);
}

exp_node FramacVisitor::makeMemberCallExpression(
  const clang::CXXMemberCallExpr* callExpr, bool* shouldDelay,
  /* statement */ list* receiver, FramaCIRGenAction::ExpressionGuard& guard)
{ 
  /*
    There are 4 kinds of member calls:
    - direct call to a non-static, non-virtual function (just need to add the
      this argument at the beginning of the list)
    - direct call to a virtual function (this argument + retrieve entry in
      virtual methode table)
    - call via a pointer-to-member to non-virtual function
    - call via a pointer-to-member to virtual function (currently unsupported)
   */
  clang::CXXMemberCallExpr::const_arg_iterator argIterEnd = callExpr->arg_end();
  const clang::Expr* thisArgument = callExpr->getImplicitObjectArgument();
  list /* expression */ arguments = NULL;
  ForwardReferenceList forwardArguments(arguments);
  const clang::Expr* callee = callExpr->getCallee()->IgnoreParens();
  // directCall will be NULL for pointer-to-member calls
  const clang::CXXMethodDecl* directCall = callExpr->getMethodDecl();
  // NB: as said above, pointer-to-member are assumed to be non-virtual.
  bool isVirtualCall = directCall ? directCall->isVirtual() : false;
  // even if the function is virtual, if we have qualified lookup, we'll end
  // up calling a specific version, without VT lookup.
  if (isVirtualCall) {
    assert(llvm::dyn_cast<const clang::MemberExpr>(callee));
    isVirtualCall =
      !static_cast<const clang::MemberExpr*>(callee)->hasQualifier();
  }
  // first, translate the receiver argument (i.e. this)
  expression argument = makeLocExpression(thisArgument, shouldDelay);
  // clang doc says that callee is a member expr
  // in fact, it contains the information whether we
  // have x.f(),  x->f() x.*f() or x->*f()
  clang::Stmt::StmtClass kind = callee->getStmtClass();
  assert (kind == clang::Stmt::BinaryOperatorClass
          || kind == clang::Stmt::MemberExprClass);
    // pointer to member call
  if (kind == clang::Stmt::BinaryOperatorClass) {
    assert(llvm::dyn_cast<const clang::BinaryOperator>(callee));
    const clang::BinaryOperator* binop =
      static_cast<const clang::BinaryOperator*>(callee);
    switch(binop->getOpcode())
      {
        case clang::BO_PtrMemD:
          argument =
            expression_cons(makeLocation(callee->getSourceRange()),
                            exp_node_Address(argument));
        case clang::BO_PtrMemI:
          break;
        default:
          std::cerr << "Unexpected opcode in method call: ";
          callee->dump();
          std::cerr << std::endl;
          exit(2);
      }
  } else {
    // normal call (either static or virtual)
    assert(llvm::dyn_cast<const clang::MemberExpr>(callee));
    const clang::MemberExpr* callee_member
      = static_cast<const clang::MemberExpr*>(callee);
    if(!callee_member->isArrow()) {
      argument =
        expression_cons(
          makeLocation(callee->getSourceRange()),
          exp_node_Address(argument));
    }
  }
  // for virtual calls, the receiver is still treated separately
  if (!isVirtualCall) forwardArguments.insertContainer(argument);

  // Now translate all the arguments
  for (clang::CXXMemberCallExpr::const_arg_iterator
      argIter = callExpr->arg_begin(); argIter != argIterEnd; ++argIter) {
    expression argument = makeLocExpression(*argIter, shouldDelay);
    forwardArguments.insertContainer(argument);
  };

  // Now, find which function to call
  // First, check whether we are in a template instantiation
  tkind templateExtension = NULL;
  if (directCall && directCall->getTemplateSpecializationKind()
      >= clang::TSK_ImplicitInstantiation) {
    clang::FunctionTemplateSpecializationInfo* info
      = directCall->getTemplateSpecializationInfo();
    if (info)
      templateExtension =
        tkind_TTemplateInstance(_clangUtils->getTemplateExtension(info));
  };
  if (!templateExtension) templateExtension = tkind_TStandard();
  if (!directCall) {
    // pointer to member call: we have a binary operator, and the
    // function pointer is the rhs part
    assert(llvm::dyn_cast<const clang::BinaryOperator>(callee) && _context);
    const clang::BinaryOperator* binop =
      static_cast<const clang::BinaryOperator*>(callee);
    expression call = makeLocExpression(binop->getRHS());
    return
      convert_result(
        guard,
        callExpr->getCallReturnType(*_context),
        exp_node_Dynamic_call(funkind_FKMethod(NULL), call, arguments),
        callExpr);

  }
  // direct call

  // Update tableForwaitingdeclarations as needed to provide functions
  // definitions in a suitable order.
  if (hasInstanceContext()) {
    if (!_tableForWaitingDeclarations.hasVisited(directCall)
        && directCall != _parents.getSFunctionDecl())
      unvisitedDecls().push_back(directCall);
    else {
      clang::Decl::Kind kindDecl =
        directCall->getDeclContext()->getDeclKind();
      if (kindDecl >= clang::Decl::firstRecord
          && kindDecl <= clang::Decl::lastRecord)
        _tableForWaitingDeclarations.ensureGeneration(
          static_cast<const clang::RecordDecl*>(
            directCall->getDeclContext()),
          unvisitedDecls());
    };
  }
  else if (shouldDelay && !*shouldDelay)
    *shouldDelay = !_tableForWaitingDeclarations.hasVisited(directCall)
      && directCall != _parents.getSFunctionDecl();
  
  qualified_name name = _clangUtils->makeQualifiedName(*directCall);
  signature sig = _clangUtils->makeSignature(*directCall);
  clang::QualType const& ret = callExpr->getCallReturnType(*_context);
  if (!isVirtualCall) {
    // static call: we just have a normal function call
    return convert_result(
      guard,ret,
      exp_node_Static_call(
        name, sig,
        _clangUtils->cv_meth(static_cast<const clang::CXXMethodDecl*>(
            directCall)), arguments, templateExtension, false),
      callExpr);
  };

  // virtual call: this is in fact an indirect call.
  // Retrieve appropriate pointer from VMT
  assert(templateExtension->tag_tkind == TSTANDARD);
  free(templateExtension);
  templateExtension = NULL;
  exp_node result =
    exp_node_Virtual_call(
      name, sig, _clangUtils->cv_meth(static_cast<const clang::CXXMethodDecl*>(
        directCall)), argument, arguments, 0, NULL, NULL);
  const_cast<RTTITable&>(_rttiTable).retrieveMethodIndex(*_clangUtils,
      callExpr->getMethodDecl()->getParent(), callExpr->getMethodDecl(),
      &result->cons_exp_node.Virtual_call.method_index,
      &result->cons_exp_node.Virtual_call.shift_object,
      &result->cons_exp_node.Virtual_call.shift_pvmt);
  return convert_result(guard,ret,result, callExpr);
}

exp_node
FramacVisitor::makeBaseToDerivedPointerCastExpression(
    const clang::CXXRecordDecl* base, qual_type baseType,
    const clang::CXXRecordDecl* derived, qual_type derivedType,
    expression expr) {
  assert(base && derived);
  RTTITable::InheritancePath inheritancePath;
  RTTITable::VirtualInheritancePath virtualInheritancePath(NULL, 0);
  _rttiTable.retrieveInheritancePathBetween(derived, base,
      inheritancePath, virtualInheritancePath, *_clangUtils);
  assert(!virtualInheritancePath.first);
  int sizeInheritancePath = (int) inheritancePath.size()-1;
  assert(sizeInheritancePath >= 0);
  exp_node nodeResult = exp_node_ShiftPointerCast(
      derivedType, baseType, reference_or_pointer_kind_RPKPointer(), expr);
  while (--sizeInheritancePath >= 0) {
    const clang::CXXRecordDecl
      *localDerived = inheritancePath[sizeInheritancePath].first;
    tkind derived_kind = _clangUtils->makeTemplateKind(localDerived);
    qual_type localDerivedType =
      qual_type_cons(
        NULL,
        typ_Pointer(
          pkind_PDataPointer(
            qual_type_cons(
              NULL,
              typ_Struct(
                _clangUtils->makeQualifiedName(*localDerived),derived_kind)))));
    exp_node tmpResult = exp_node_ShiftPointerCast(
      localDerivedType, qual_type_dup(localDerivedType),
      reference_or_pointer_kind_RPKPointer(),
      expression_cons(copy_loc(expr->eloc), nodeResult));
    qual_type tmp=nodeResult->cons_exp_node.ShiftPointerCast.cast_type;
    nodeResult->cons_exp_node.ShiftPointerCast.cast_type
      = tmpResult->cons_exp_node.ShiftPointerCast.cast_type;
    tmpResult->cons_exp_node.ShiftPointerCast.cast_type = tmp;
    nodeResult = tmpResult;
  };
  return nodeResult;
}

exp_node FramacVisitor::makeBaseToDerivedReferenceCastExpression(
    const clang::CXXRecordDecl* base, qual_type baseType,
    const clang::CXXRecordDecl* derived, qual_type derivedType,
    expression expr) {
  assert(base && derived);
  RTTITable::InheritancePath inheritancePath;
  RTTITable::VirtualInheritancePath virtualInheritancePath(NULL, 0);
  _rttiTable.retrieveInheritancePathBetween(derived, base,
      inheritancePath, virtualInheritancePath, *_clangUtils);
  assert(!virtualInheritancePath.first);
  int sizeInheritancePath = (int) inheritancePath.size()-1;
  assert(sizeInheritancePath >= 0);
  exp_node nodeResult = exp_node_ShiftPointerCast(
      qual_type_cons(NULL, typ_LVReference(pkind_PDataPointer(derivedType))),
      baseType, reference_or_pointer_kind_RPKReference(), expr);
  while (--sizeInheritancePath >= 0) {
    const clang::CXXRecordDecl
      *localDerived = inheritancePath[sizeInheritancePath].first;
    tkind derived_kind = _clangUtils->makeTemplateKind(localDerived);
    qual_type localDerivedType =
      qual_type_cons(
        NULL,
        typ_LVReference(
          pkind_PDataPointer(
            qual_type_cons(
              NULL,
              typ_Struct(
                _clangUtils->makeQualifiedName(*localDerived),derived_kind)))));
    exp_node tmpResult = exp_node_ShiftPointerCast(
      localDerivedType,
      qual_type_dup(localDerivedType->plain_type->cons_typ.LVReference
          .kind->cons_pkind.PDataPointer.subtype),
      reference_or_pointer_kind_RPKReference(),
      expression_cons(copy_loc(expr->eloc), nodeResult));
    qual_type tmp=nodeResult->cons_exp_node.ShiftPointerCast.cast_type;
    nodeResult->cons_exp_node.ShiftPointerCast.cast_type
      = tmpResult->cons_exp_node.ShiftPointerCast.cast_type;
    tmpResult->cons_exp_node.ShiftPointerCast.cast_type = tmp;
    nodeResult = tmpResult;
  };
  return nodeResult;
}

exp_node FramacVisitor::makeDerivedToBaseCastExpression(
  ptr_or_ref kind,
  const clang::CXXRecordDecl* derived, qual_type baseType,
  const clang::CXXRecordDecl* base, expression expr, bool* shouldDelay)
{ assert(base && derived);
  if (hasInstanceContext()) {
    _tableForWaitingDeclarations.ensureGeneration(derived, unvisitedDecls());
    _tableForWaitingDeclarations.ensureGeneration(base, unvisitedDecls());
  }
  else if (shouldDelay && !*shouldDelay) {
    *shouldDelay = !_tableForWaitingDeclarations.hasVisited(derived)
        || !_tableForWaitingDeclarations.hasVisited(base);
  };
  RTTITable::InheritancePath inheritancePath;
  RTTITable::VirtualInheritancePath virtualInheritancePath(NULL, 0);
  _rttiTable.retrieveInheritancePathBetween(
    derived, base, inheritancePath, virtualInheritancePath, *_clangUtils);

  exp_node nodeResult = NULL;
  int sizeInheritancePath = (int) inheritancePath.size()-1;
  // if we don't have a virtual base, the non-virtual inheritance path
  // cannot be empty
  assert(virtualInheritancePath.first || sizeInheritancePath >= 0);
  // if the base class is part of a virtual base of derived (either
  // directly a virtual base, or a class the virtual base inherits from),
  // we first perform an offset computation, potentially followed by
  // a sequence of non-virtual casts
  if (virtualInheritancePath.first) {
    // const clang::ClassTemplateSpecializationDecl* TSD = NULL;
    tkind tk = _clangUtils->makeTemplateKind(derived);
    auto pk_derived =
      pkind_PDataPointer(
        qual_type_cons(
          NULL, typ_Struct(_clangUtils->makeQualifiedName(*derived),tk)));
    auto t_derived =
      kind == PTR ? typ_Pointer(pk_derived) : typ_LVReference(pk_derived);
    qual_type derivedType = qual_type_cons(NULL, t_derived);
    const clang::CXXRecordDecl* derivedClass = virtualInheritancePath.first;
    qual_type localDerivedType = baseType;
    if (sizeInheritancePath >= 0) {
      localDerivedType = qual_type_dup(derivedType);
    };
    auto rpk =
      kind == PTR ?
      reference_or_pointer_kind_RPKVirtualBasePointer(
        virtualInheritancePath.second, derivedType, false) :
      reference_or_pointer_kind_RPKVirtualBaseReference(
        virtualInheritancePath.second, derivedType);
    nodeResult = exp_node_PointerCast(localDerivedType, rpk, expr);
    if (virtualInheritancePath.second < 0) {
      auto id = nodeResult->cons_exp_node.PointerCast.ref_pointer;
      auto idx =
        kind == PTR ?
        &id->cons_reference_or_pointer_kind.RPKVirtualBasePointer.base_index:
        &id->cons_reference_or_pointer_kind.RPKVirtualBaseReference.base_index;
      const_cast<RTTITable&>(_rttiTable).retrieveBaseIndex(
        *_clangUtils,
        derived, const_cast<clang::CXXRecordDecl*>(derivedClass), idx);
    }
  };
  // now compute the non-virtual part of the pointer cast.
  if (sizeInheritancePath >= 0) {
    auto exp = nodeResult?expression_cons(copy_loc(expr->eloc),nodeResult):expr;
    auto rpk =
      kind == PTR ?
      reference_or_pointer_kind_RPKStaticBasePointer ():
      reference_or_pointer_kind_RPKStaticBaseReference ();
    nodeResult = exp_node_PointerCast(baseType, rpk, exp);
  }
  for (int index = 0; index < sizeInheritancePath; ++index) {
    const clang::CXXRecordDecl
      *localBase = inheritancePath[index].first;
    auto tk = _clangUtils->makeTemplateKind(localBase);
    auto pk_data =
      pkind_PDataPointer(
        qual_type_cons(
          NULL,typ_Struct(_clangUtils->makeQualifiedName(*localBase), tk)));
    auto typ =
      kind == PTR ? typ_Pointer(pk_data) : typ_LVReference(pk_data);
    qual_type localBaseType = qual_type_cons(NULL,typ);
    auto rpk =
      kind == PTR ?
      reference_or_pointer_kind_RPKStaticBasePointer() :
      reference_or_pointer_kind_RPKStaticBaseReference();
    exp_node tmpResult =
      exp_node_PointerCast(
        localBaseType, rpk, nodeResult->cons_exp_node.PointerCast.sub);
    nodeResult->cons_exp_node.PointerCast.sub =
        expression_cons(copy_loc(expr->eloc), tmpResult);
  };
  return nodeResult;
}

exp_node FramacVisitor::makeCastExpression(
  const clang::CastExpr* castExpr,
  bool* shouldDelay, /* statement */ list* receiver,
  FramaCIRGenAction::ExpressionGuard& guard) {
  expression subExpr =
    makeLocExpression(castExpr->getSubExpr(), shouldDelay, receiver);
  qual_type resultType =
    makeDefaultType(castExpr->getExprLoc(), castExpr->getType());
  switch (castExpr->getCastKind()) {
    case clang::CK_BitCast:
      return guard.setAssignResult(exp_node_Unary_operator(
           uokind_UOCastC(resultType),subExpr), castExpr);
    case clang::CK_LValueBitCast:
      return guard.setAssignResult(exp_node_Dereference(
        expression_cons(copy_loc(subExpr->eloc), exp_node_Unary_operator(
          uokind_UOCastC(qual_type_cons(NULL, typ_Pointer(pkind_PDataPointer(
            resultType)))), expression_cons(copy_loc(subExpr->eloc),
              exp_node_Address(subExpr))))), castExpr);
    case clang::CK_ToUnion:
    case clang::CK_VectorSplat:
    case clang::CK_CPointerToObjCPointerCast:
    case clang::CK_BlockPointerToObjCPointerCast:
    case clang::CK_AnyPointerToBlockPointerCast:
    case clang::CK_ObjCObjectLValueCast:
    case clang::CK_ARCProduceObject:
    case clang::CK_ARCConsumeObject:
    case clang::CK_ARCReclaimReturnedObject:
    case clang::CK_ARCExtendBlockObject:
    case clang::CK_AtomicToNonAtomic:
    case clang::CK_NonAtomicToAtomic:
    case clang::CK_CopyAndAutoreleaseBlockObject:
    case clang::CK_BuiltinFnToFnPtr:
      std::cerr << "Unsupported cast:";
#if CLANG_VERSION_MAJOR >= 11
      castExpr->dump(llvm::errs(), *_context);
#else
      castExpr->dump(llvm::errs(), _context->getSourceManager());
#endif
      std::cerr << "\nAborting\n";
      exit(2);
    case clang::CK_FloatingRealToComplex:
    case clang::CK_FloatingComplexToReal:
    case clang::CK_FloatingComplexToBoolean:
    case clang::CK_FloatingComplexCast:
    case clang::CK_FloatingComplexToIntegralComplex:
    case clang::CK_IntegralRealToComplex:
    case clang::CK_IntegralComplexToReal:
    case clang::CK_IntegralComplexToBoolean:
    case clang::CK_IntegralComplexCast:
    case clang::CK_IntegralComplexToFloatingComplex:
      std::cerr << "Unsupported context cast:";
#if CLANG_VERSION_MAJOR >= 11
      castExpr->dump(llvm::errs(), *_context);
#else
      castExpr->dump(llvm::errs(), _context->getSourceManager());
#endif
      std::cerr << "\nAborting\n";
      exit(2);
    case clang::CK_ToVoid:
      return
        guard.setAssignResult(
          exp_node_Unary_operator(uokind_UOCastToVoid(), subExpr), castExpr);
    case clang::CK_LValueToRValue:
      // does not exactly denote a cast from "reference to T" to "T":
      // we need to use another indicator to introduce the appropriate
      // transformation, see ClangUtils::lvalHasRefType for more information
    case clang::CK_NoOp:
      return
        guard.setAssignResult(
          exp_node_Unary_operator(uokind_UOCastNoEffect(resultType), subExpr),
          castExpr);
    case clang::CK_BaseToDerived:
      { qual_type baseType =
          makeDefaultType(
            castExpr->getSubExpr()->getExprLoc(),
            castExpr->getSubExpr()->getType());
        if (castExpr->getType()->isPointerType())
          /* isArrayType(), isFunctionType(), isPointerType(),
             isReferenceType(), isRecordType(), isUnionType(),
             isEnumeralType(), isMemberPointerType() */
          return guard.setAssignResult(
            makeBaseToDerivedPointerCastExpression(
              castExpr->getSubExpr()->getType()->getPointeeCXXRecordDecl(),
              baseType, castExpr->getType()->getPointeeCXXRecordDecl(),
              resultType, subExpr), castExpr);
        return guard.setAssignResult(
          makeBaseToDerivedReferenceCastExpression(
            castExpr->getSubExpr()->getType()->getAsCXXRecordDecl(), baseType,
            castExpr->getType()->getAsCXXRecordDecl(), resultType, subExpr),
          castExpr);
      }
    case clang::CK_UncheckedDerivedToBase:
    case clang::CK_DerivedToBase:
      if (castExpr->getType()->isPointerType())
        /* isArrayType(), isFunctionType(), isPointerType(),
           isReferenceType(), isRecordType(), isUnionType(),
           isEnumeralType(), isMemberPointerType() */
          return guard.setAssignResult(
            makeDerivedToBaseCastExpression(
              PTR,
              castExpr->getSubExpr()->getType()->getPointeeCXXRecordDecl(),
              resultType, castExpr->getType()->getPointeeCXXRecordDecl(),
              subExpr, shouldDelay),
            castExpr);
        return guard.setAssignResult(
          makeDerivedToBaseCastExpression(
            REF,
            castExpr->getSubExpr()->getType()->getAsCXXRecordDecl(),
            qual_type_cons(NULL, typ_LVReference(pkind_PDataPointer(resultType))),
            castExpr->getType()->getAsCXXRecordDecl(), subExpr,
            shouldDelay),
          castExpr);
    case clang::CK_ArrayToPointerDecay:
    case clang::CK_FunctionToPointerDecay:
    case clang::CK_NullToMemberPointer:
    case clang::CK_NullToPointer:
      if (castExpr->getType()->isReferenceType())
        /* isArrayType(), isFunctionType(), isPointerType(),
           isReferenceType(), isRecordType(), isUnionType(),
           isEnumeralType(), isMemberPointerType() */
        return guard.setAssignResult(exp_node_PointerCast(resultType,
              reference_or_pointer_kind_RPKReference(), subExpr), castExpr);
      return guard.setAssignResult(exp_node_PointerCast(resultType,
            reference_or_pointer_kind_RPKPointer(), subExpr), castExpr);
    case clang::CK_IntegralToPointer:
    case clang::CK_ReinterpretMemberPointer:
      return guard.setAssignResult(exp_node_PointerCast(resultType,
            reference_or_pointer_kind_RPKPointer(), subExpr), castExpr);
    case clang::CK_MemberPointerToBoolean:
    case clang::CK_PointerToBoolean:
    case clang::CK_IntegralToBoolean:
    case clang::CK_FloatingToBoolean:
      return guard.setAssignResult(exp_node_Unary_operator(
          uokind_UOCastInteger(resultType, IBOOL)/*, AKRVALUE*/, subExpr),
        castExpr);
    case clang::CK_PointerToIntegral:
    case clang::CK_IntegralCast:
    case clang::CK_IntegralToFloating:
    case clang::CK_FloatingToIntegral:
    case clang::CK_FloatingCast:
      if (resultType->plain_type->tag_typ==ENUM)
        return guard.setAssignResult(exp_node_Unary_operator(
          uokind_UOCastEnum(resultType, resultType->plain_type
            ->cons_typ.Enum.kind), /*AKRVALUE,*/ subExpr), castExpr);
      if (resultType->plain_type->tag_typ==INT)
        return guard.setAssignResult(exp_node_Unary_operator(
          uokind_UOCastInteger(resultType, resultType->plain_type
            ->cons_typ.Int.kind), /* AKRVALUE,*/ subExpr), castExpr);
      if (resultType->plain_type->tag_typ==FLOAT)
        return guard.setAssignResult(exp_node_Unary_operator(
          uokind_UOCastFloat(resultType, resultType->plain_type
            ->cons_typ.Float.kind), /* AKRVALUE,*/ subExpr), castExpr);
      return guard.setAssignResult(exp_node_Unary_operator(
          uokind_UOCastC(resultType),subExpr), castExpr);
    case clang::CK_UserDefinedConversion: /* should be translated */
    case clang::CK_ConstructorConversion: {
      exp_node content = subExpr->econtent;
      free_qual_type(resultType);
      free_location(subExpr->eloc);
      free(subExpr);
      return guard.setAssignResult(content, castExpr);
    }
    case clang::CK_Dynamic:
      return
        guard.setAssignResult(
          makeDynamicCastExpressionAux(
            _clangUtils->getBeginLoc(*castExpr), castExpr->getType(),
            castExpr->getSubExpr()->getType(), subExpr),
          castExpr);
    default:
      { std::cerr << "Unsupported context cast:";
#if CLANG_VERSION_MAJOR >= 11
        castExpr->dump(llvm::errs(), *_context);
#else
        castExpr->dump(llvm::errs(), _context->getSourceManager());
#endif
        std::cerr << "\nAborting\n";
        exit(2);
      };
  };
}

exp_node FramacVisitor::makeDynamicCastExpressionAux(
  const clang::SourceLocation loc,
  const clang::QualType result_type,
  const clang::QualType orig_type,
  expression subExpr)
{
  bool isReferenceType = result_type->isReferenceType();
  auto resultType = makeDefaultType(loc, result_type);
  init_expr init_arg =
    init_expr_Single_init(
      !isReferenceType ?
      subExpr:
      expression_cons(copy_loc(subExpr->eloc), exp_node_Address(subExpr)));
  const char* temp = mk_tmp_name();
  expression arg =
    expression_cons(
      copy_loc(subExpr->eloc),
      exp_node_Temporary(
        temp,
        qual_type_cons(
          NULL,
          typ_Pointer(
            pkind_PDataPointer(
              makeDefaultType(loc, orig_type->getPointeeType())))),
        init_arg, true));
  auto c_orig_type = makeDefaultType(loc,orig_type);
  auto orig_var =
    expression_cons(
      copy_loc(subExpr->eloc),
      exp_node_Variable(
        variable_Local(qualified_name_cons(NULL,strdup(temp)))));
  // auto orig_exp =
  //   isReferenceType?
  //   expression_cons(copy_loc(subExpr->eloc), exp_node_Dereference(orig_var)):
  //   orig_var;
  auto pvmt =
    _rttiTable.getPvmt(
      *_clangUtils,orig_type->getPointeeCXXRecordDecl(), orig_var);
  auto casted_exp =
    isReferenceType?
    expression_cons(copy_loc(subExpr->eloc), exp_node_Dereference(arg)) : arg;
  auto rpk_res =
    (isReferenceType?
     reference_or_pointer_kind_RPKDynamicReference:
     reference_or_pointer_kind_RPKDynamicPointer)
    (c_orig_type, pvmt);
  return exp_node_PointerCast(resultType, rpk_res, casted_exp);
}

exp_node FramacVisitor::makeDynamicCastExpression(
    const clang::CXXDynamicCastExpr* castExpr, bool* shouldDelay,
    /* statement */ list* receiver, FramaCIRGenAction::ExpressionGuard& guard) {
  // expression subExpr =
  //   makeLocExpression(castExpr->getSubExpr(), shouldDelay, receiver);
  // qual_type resultType =
  //   makeDefaultType(castExpr->getExprLoc(), castExpr->getType());

  const clang::CXXRecordDecl* base = castExpr->getSubExpr()->getType()
      .getTypePtr()->getPointeeCXXRecordDecl();
  const clang::CXXRecordDecl* derived = castExpr->getType().getTypePtr()
      ->getPointeeCXXRecordDecl();
  if (!base)
    base = castExpr->getSubExpr()->getType().getTypePtr()
        ->getCanonicalTypeInternal().getTypePtr()->getAsCXXRecordDecl();
  if (!derived)
    derived = castExpr->getType().getTypePtr()
        ->getCanonicalTypeInternal().getTypePtr()->getAsCXXRecordDecl();
  if (base && derived) {
    if (hasInstanceContext()) {
      _tableForWaitingDeclarations.ensureGeneration(derived, unvisitedDecls());
      _tableForWaitingDeclarations.ensureGeneration(base, unvisitedDecls());
    }
    else if (shouldDelay && !*shouldDelay) {
      *shouldDelay = !_tableForWaitingDeclarations.hasVisited(derived)
          || !_tableForWaitingDeclarations.hasVisited(base);
    };
  };
  return
    guard.setAssignResult(
      makeDynamicCastExpressionAux(
        _clangUtils->getBeginLoc(*castExpr), castExpr->getType(),
        castExpr->getSubExpr()->getType(),
        makeLocExpression(castExpr->getSubExpr())),
      castExpr);
}

exp_node FramacVisitor::makeCompoundLiteralExpression(
    const clang::CompoundLiteralExpr* compoundExpr,
    bool* shouldDelay, /* statement */ list* receiver,
    bool hasImplicitThisStar, FramaCIRGenAction::ExpressionGuard& guard) {
  clang::QualType ctype = compoundExpr->getType();
  qual_type typ =
    makeDefaultType(compoundExpr->getExprLoc(), ctype);
  expression address = NULL;
  if (hasImplicitThisStar) {
    expression lval = guard.releaseExpr ();
    location loc = makeLocation(compoundExpr->getSourceRange());
    lval = getNodeReference(lval, compoundExpr->getType());
    address = expression_cons(loc,exp_node_Address(lval));
  };
  init_expr init =
    makeInitExpr(ctype, compoundExpr->getInitializer(), shouldDelay);
  const char* temp = mk_tmp_name();
  exp_node res = exp_node_Temporary(temp, typ, init, false);
  if (address) {
    location loc = makeLocation(compoundExpr->getSourceRange());
    res = exp_node_Assign(address, expression_cons(loc,res));
  };
  return res;
}

exp_node FramacVisitor::makeDeclRefExpression(
    const clang::DeclRefExpr* variableExpr, bool* shouldDelay,
    /* statement */ list* receiver, FramaCIRGenAction::ExpressionGuard& guard) {
  variable result = NULL;
  clang::Decl::Kind kind = variableExpr->getDecl()->getKind();
  if (kind == clang::Decl::Field) {
    std::cerr << "Unsupported field access:";
#if CLANG_VERSION_MAJOR >= 11
    variableExpr->dump(llvm::errs(), *_context);
#else
    variableExpr->dump(llvm::errs(), _context->getSourceManager());
#endif
    std::cerr << "\nAborting\n";
    exit(2);
  }
  else if (kind >= clang::Decl::firstFunction
      && kind <= clang::Decl::lastFunction) {
    assert(llvm::dyn_cast<clang::FunctionDecl>(variableExpr->getDecl()));
    const clang::FunctionDecl* function
      = static_cast<const clang::FunctionDecl*>(variableExpr->getDecl());
    if (hasInstanceContext()) {
      if (!_tableForWaitingDeclarations.hasVisited(function)
            && function != _parents.getSFunctionDecl())
        unvisitedDecls().push_back(function);
      else {
        clang::Decl::Kind kindDecl = function->getDeclContext()->getDeclKind();
        if (kindDecl >= clang::Decl::firstRecord
            && kindDecl <= clang::Decl::lastRecord)
          _tableForWaitingDeclarations.ensureGeneration(
            static_cast<const clang::RecordDecl*>(function->getDeclContext()),
            unvisitedDecls());
      };
    }
    else if (shouldDelay && !*shouldDelay)
      *shouldDelay = !_tableForWaitingDeclarations.hasVisited(function)
            && function != _parents.getSFunctionDecl();
    tkind templateExtension = NULL;
    if (function->getTemplateSpecializationKind()
        >= clang::TSK_ImplicitInstantiation) {
      clang::FunctionTemplateSpecializationInfo* info
        = function->getTemplateSpecializationInfo();
      if (info)
        templateExtension =
          tkind_TTemplateInstance(_clangUtils->getTemplateExtension(info));
    };
    if (!templateExtension)
      templateExtension = tkind_TStandard();
    funkind fkind;
    if (kind == clang::Decl::CXXConstructor)
      fkind = funkind_FKConstructor(true);
    else if (kind == clang::Decl::CXXDestructor)
      fkind = funkind_FKDestructor(true);
    else if ((kind >= clang::Decl::firstCXXMethod)
             && (kind <= clang::Decl::lastCXXMethod)) {
      if (function->getCanonicalDecl()->getStorageClass() != clang::SC_Static)
        fkind = _clangUtils->cv_meth(static_cast<const clang::CXXMethodDecl*>(
          function));
      else
        fkind = funkind_FKFunction();
    }
    else
      fkind = funkind_FKFunction();
    signature sig = _clangUtils->makeSignature(*function);
    qualified_name qual = _clangUtils->makeQualifiedName(*function);
    if (fkind->tag_funkind == FKCONSTRUCTOR && strlen(qual->decl_name) == 0) {
      free(const_cast<char*>(qual->decl_name));
      qual->decl_name = strdup("constructor-special");
    };
    result = variable_CodePointer(qual, sig, fkind, 
      function->isInExternCContext(), templateExtension);
  }
  else if (kind == clang::Decl::Var) {
    assert(llvm::dyn_cast<clang::VarDecl>(variableExpr->getDecl()));
    const clang::VarDecl* var
        = static_cast<const clang::VarDecl*>(variableExpr->getDecl());
    if (var->hasLocalStorage())
      result = variable_Local(_clangUtils->makeQualifiedName(*var));
    else { // var->hasGlobalStorage()
      result = variable_Global(_clangUtils->makeQualifiedName(*var));
      if (shouldDelay && !*shouldDelay) {
        *shouldDelay = !_tableForWaitingDeclarations.hasVisited(var);
      };
    }
  }
  else if (kind == clang::Decl::ImplicitParam)
    result = variable_FunctionParameter(strdup(variableExpr
        ->getDecl()->getName().str().c_str()));
  else if (kind == clang::Decl::ParmVar)
    result = variable_FunctionParameter(strdup(variableExpr
        ->getDecl()->getName().str().c_str()));
  else if (kind == clang::Decl::EnumConstant) {
    assert(llvm::dyn_cast<clang::EnumConstantDecl>(variableExpr->getDecl()));
    const clang::EnumConstantDecl* enumDecl
      = static_cast<const clang::EnumConstantDecl*>(variableExpr->getDecl());
    const clang::QualType t = enumDecl->getType();
    assert (llvm::dyn_cast<clang::EnumType const>(t));
    const clang::EnumType* enumType =
      static_cast<const clang::EnumType*>(t.getTypePtr());
    const clang::EnumDecl* declEnum = enumType->getDecl();
    if (hasInstanceContext()) {
      if (!_tableForWaitingDeclarations.hasVisited(declEnum))
        unvisitedDecls().push_back(declEnum);
      else {
        clang::Decl::Kind kindDecl = declEnum->getDeclContext()->getDeclKind();
        if (kindDecl >= clang::Decl::firstRecord
            && kindDecl <= clang::Decl::lastRecord)
          _tableForWaitingDeclarations.ensureGeneration(
            static_cast<const clang::RecordDecl*>(declEnum->getDeclContext()),
            unvisitedDecls());
      };
    }
    else if (shouldDelay && !*shouldDelay)
      *shouldDelay = !_tableForWaitingDeclarations.hasVisited(
          enumType->getDecl());
    return
      guard.setAssignResult(
        exp_node_Constant(
          compilation_constant_EnumCst(
            _clangUtils->makeQualifiedName(*variableExpr->getDecl()),
            ekind_cons(
              _clangUtils->makeQualifiedName(*declEnum),
              Clang_utils::isExternCContext(enumType->getDecl())),
            (int64_t)enumDecl->getInitVal().getLimitedValue(UINT64_MAX))),
        variableExpr);
  }
  else if (kind == clang::Decl::IndirectField) {
    std::cerr << "Unsupported indirect field:";
#if CLANG_VERSION_MAJOR >= 11
    variableExpr->dump(llvm::errs(), *_context);
#else
    variableExpr->dump(llvm::errs(), _context->getSourceManager());
#endif
    std::cerr << "\nAborting\n";
    exit(2);
  }
  else {
    std::cerr << "Unsupported reference expression:";
#if CLANG_VERSION_MAJOR >= 11
    variableExpr->dump(llvm::errs(), *_context);
#else
    variableExpr->dump(llvm::errs(), _context->getSourceManager());
#endif
    std::cerr << "\nAborting\n";
    exit(2);
  };
  exp_node eres = exp_node_Variable(result);
  if (_clangUtils->lvalHasRefType(variableExpr)) {
    location loc = makeLocation(variableExpr->getSourceRange());
    expression exp_res = expression_cons(loc,eres);
    eres = exp_node_Unary_operator(uokind_UOCastDeref(),exp_res);
  }
  return guard.setAssignResult(eres, variableExpr);
}

exp_node FramacVisitor::makeMaterializeTemporaryExpression(
    const clang::MaterializeTemporaryExpr* tmpExpr, bool* shouldDelay,
    /* statement */ list* receiver, FramaCIRGenAction::ExpressionGuard& guard) {
  expression oldThisStar = _implicitThisStar;
  location loc = makeLocation(tmpExpr->getSourceRange());
  const char* tmp = mk_materialized_tmp_name();
  qualified_name qtmp = qualified_name_cons(NULL,tmp);
  expression etmp =
    expression_cons(loc, exp_node_Variable(variable_Local(qtmp)));
  _implicitThisStar = etmp;
  const clang::Expr* e =
#if CLANG_VERSION_MAJOR >= 10
    tmpExpr->getSubExpr();
#else
    tmpExpr->GetTemporaryExpr();
#endif
  init_expr temp = makeInitExpr(tmpExpr->getType(), e, shouldDelay);
  qual_type temp_type =
    makeDefaultType(tmpExpr->getExprLoc(), tmpExpr->getType());
  _implicitThisStar = oldThisStar;
  return guard.setAssignResult(exp_node_Temporary(tmp,temp_type,temp,false),
                               tmpExpr);
}

exp_node
FramacVisitor::makeBindTemporaryExpression(
    const clang::CXXBindTemporaryExpr* tmp, bool* shouldDelay,
    /* statement */ list* receiver, bool hasImplicitThisStar,
    FramaCIRGenAction::ExpressionGuard& guard) {
  expression oldThisStar = _implicitThisStar;
  location loc = makeLocation(tmp->getSourceRange());
  // location locCopy = copy_loc(loc);
  const char* tmp_name = NULL;
  expression etmp = NULL;
  if (hasImplicitThisStar) {
    etmp = guard.releaseExpr();
  } else {
    tmp_name = mk_tmp_name();
    qualified_name qtmp = qualified_name_cons(NULL,tmp_name);
    etmp =
      expression_cons(
        loc, exp_node_Variable(variable_Local(qtmp)));
  }
  _implicitThisStar = etmp;
  // expression addr = expression_cons(locCopy,
  //     exp_node_Address(expression_dup(etmp)));
  init_expr temp = makeInitExpr(tmp->getType(),tmp->getSubExpr(), shouldDelay);
  qual_type temp_type = makeDefaultType(tmp->getExprLoc(), tmp->getType());
  _implicitThisStar = oldThisStar;
  if (tmp_name)
    return guard.setAssignResult(
        exp_node_Temporary(tmp_name,temp_type,temp, false),tmp);
  else if (temp->tag_init_expr == SINGLE_INIT)
    return
      guard.setAssignResult(
        temp->cons_init_expr.Single_init.definition->econtent,tmp);
  else {
    tmp_name = mk_tmp_name();
    return guard.setAssignResult(
        exp_node_Temporary(tmp_name,temp_type,temp,false),tmp);
  }
}

exp_node FramacVisitor::makeUnaryOperator(
    const clang::UnaryOperator* unaryExpr, bool* shouldDelay,
    /* statement */ list* receiver, FramaCIRGenAction::ExpressionGuard& guard) {
  uokind unaryKind = NULL;
  // assign_kind assignment = AKRVALUE;
  switch (unaryExpr->getOpcode()) {
    case clang::UO_PostInc:
      unaryKind = uokind_UOPostInc(); /* assignment = AKASSIGN; */
      break;
    case clang::UO_PostDec:
      unaryKind = uokind_UOPostDec(); /* assignment = AKASSIGN; */
      break;
    case clang::UO_PreInc:
      unaryKind = uokind_UOPreInc(); /* assignment = AKASSIGN; */
      break;
    case clang::UO_PreDec:
      unaryKind = uokind_UOPreDec(); /* assignment = AKASSIGN; */
      break;
    case clang::UO_AddrOf:
      { expression esubexpr = 
          makeLocExpression(unaryExpr->getSubExpr(),shouldDelay,receiver);
        return guard.setAssignResult(exp_node_Address(esubexpr), unaryExpr);
      };
    case clang::UO_Deref:
      { exp_node subexpr = makeExpression(unaryExpr->getSubExpr(),
                                          shouldDelay,receiver);
        expression esubexpr = expression_cons(makeLocation(
              unaryExpr->getSubExpr()->getSourceRange()), subexpr);
        return guard.setAssignResult( exp_node_Dereference(esubexpr),unaryExpr);
      };
    case clang::UO_Plus:
      return guard.setAssignResult(makeExpression(unaryExpr->getSubExpr(),
          shouldDelay,receiver), unaryExpr);
    case clang::UO_Minus:
      unaryKind = uokind_UOOpposite(); /* assignment = AKRVALUE; */
      break;
    case clang::UO_Not:
      unaryKind = uokind_UOBitNegate(); /* assignment = AKRVALUE; */
      break;
    case clang::UO_LNot:
      unaryKind = uokind_UOLogicalNegate(); /* assignment = AKRVALUE; */
      break;
    case clang::UO_Extension:
      return guard.setAssignResult(makeExpression(unaryExpr->getSubExpr(),
        shouldDelay, receiver), unaryExpr);
    default:
      std::cerr << "Unsupported unary expressions:";
#if CLANG_VERSION_MAJOR >= 11
      unaryExpr->dump(llvm::errs(), *_context);
#else
      unaryExpr->dump(llvm::errs(), _context->getSourceManager());
#endif
      std::cerr << "\nAborting\n";
      exit(2);
  };
  exp_node subexpr = makeExpression(unaryExpr->getSubExpr(), shouldDelay,
      receiver);
  expression esubexpr = expression_cons(makeLocation(
      unaryExpr->getSubExpr()->getSourceRange()), subexpr);
  return guard.setAssignResult( exp_node_Unary_operator(unaryKind,
      /* assignment, */ esubexpr), unaryExpr);
}

exp_node
FramacVisitor::makeConstantArrayInitExpression(
    const clang::ConstantArrayType& arrayType, const clang::Expr* expr,
    expression lvalueThisStar, bool* shouldDelay,
    /* statement */ list* receiver) {
  const llvm::APInt& size = arrayType.getSize();
  ForwardReferenceList forwardStmts(*receiver);
  clang::SourceLocation sloc = expr->getExprLoc();
  qual_type typ = makeDefaultType(sloc, arrayType.getElementType());
  location defaultLoc = makeLocation(sloc);

  const char* temp = mk_tmp_name();
  forwardStmts.insertContainer(statement_VarDecl(
    defaultLoc, temp, qual_type_cons(NULL,
      typ_Pointer(pkind_PDataPointer(typ))),
    opt_some_container(init_expr_Single_init(lvalueThisStar))));

  clang::ImplicitValueInitExpr subExpr(arrayType.getElementType());
  /* statement */ list elementResult = NULL;
  _implicitThisStar = expression_cons(copy_loc(defaultLoc),
    exp_node_Dereference(expression_cons(copy_loc(defaultLoc),
      exp_node_Binary_operator(BOPLUS, AKRVALUE,
        expression_cons(copy_loc(defaultLoc), exp_node_Variable(
          variable_Local(qualified_name_cons(NULL, strdup(temp))))),
        expression_cons(copy_loc(defaultLoc), exp_node_Variable(
          variable_Local(qualified_name_cons(NULL,
                strdup("_frama_c_index")))))))));
  exp_node init_res = makeExpression(&subExpr, shouldDelay, &elementResult);
  if (init_res) {
    ForwardReferenceList subForwardStmts(elementResult);
    statement initPart = statement_Expression(
      copy_loc(defaultLoc), expression_cons(copy_loc(defaultLoc),
        _implicitThisStar
          ? exp_node_Assign(_implicitThisStar, expression_cons(
                copy_loc(defaultLoc), init_res))
          : init_res));
    subForwardStmts.insertContainer(initPart);
  }
  else if (_implicitThisStar) {
    free_expression(_implicitThisStar);
    _implicitThisStar = NULL;
  };
  forwardStmts.insertContainer(
    statement_For(
      copy_loc(defaultLoc),
      init_statement_IVarDecl(
        cons_container(
          init_declarator_cons(
            strdup("_frama_c_index"),
            qual_type_cons(NULL, typ_Int(IINT)),
            opt_some_container(
              init_expr_Single_init(makeIntLiteral(defaultLoc,IINT,0)))),
          NULL)),
      opt_some_container(
        expression_cons(
          copy_loc(defaultLoc),
          exp_node_Binary_operator(
            BOLESS, AKRVALUE,
            expression_cons(
              copy_loc(defaultLoc),
              exp_node_Variable(
                variable_Local(
                  qualified_name_cons(NULL, strdup("_frama_c_index"))))),
            makeIntLiteral(
              defaultLoc,IINT, (int64_t) size.getLimitedValue(UINT64_MAX))))),
      incr_statement_CExpression(
        expression_cons(
          copy_loc(defaultLoc),
          exp_node_Unary_operator(
            uokind_UOPostInc(),
            expression_cons(
              copy_loc(defaultLoc),
              exp_node_Variable(
                variable_Local(
                  qualified_name_cons(NULL, strdup("_frama_c_index")))))))),
      statement_Block(copy_loc(defaultLoc), elementResult),
      NULL /* annot = should unroll the loop */));
  return exp_node_Dereference(expression_cons(copy_loc(defaultLoc),
    exp_node_Variable(variable_Local(qualified_name_cons(NULL,
      strdup(temp))))));
}
  
exp_node FramacVisitor::makeRecordInitExpression(
  const clang::RecordDecl& record,
  const clang::Expr* expr, expression lvalueThisStar,
  bool* shouldDelay, /* statement */ list* receiver) {
  const clang::CXXRecordDecl* cxxRecord
      = llvm::dyn_cast<clang::CXXRecordDecl>(&record);
  if (cxxRecord && cxxRecord->hasDefaultConstructor()) {
    // const char* temp = mk_tmp_name();
    location defaultLoc = makeLocation(expr->getSourceRange());
    expression tmpvar = expression_cons(defaultLoc,
        exp_node_Address(lvalueThisStar));
    qualified_name name = _clangUtils->makeQualifiedName(record);
    // qualified_name recordName = qualified_name_dup(name);
    /* qualification */ list* endQual = &name->prequalification;
    while (*endQual) endQual = &(*endQual)->next;
    const clang::ClassTemplateSpecializationDecl* TSD
      = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(cxxRecord);
    if (TSD)
      *endQual =
        cons_container(
          qualification_QTemplateInstance(
            name->decl_name, _clangUtils->getTemplateExtension(TSD)),
          NULL);
    else
      *endQual = cons_container(qualification_QStructOrClass(
          name->decl_name), NULL);
    name->decl_name = strdup("constructor-special");
    signature sig =
      signature_cons(qual_type_cons(NULL, typ_Void()), NULL, false);
    return exp_node_Static_call(
      name, sig, funkind_FKConstructor(true),
      cons_container(tmpvar, NULL),
      tkind_TStandard(),
      false /* extern_c_call */);
  }
  else {
    qual_type typ = makeDefaultType(expr->getExprLoc(), expr->getType());
    location defaultLoc = makeLocation(expr->getSourceRange());
    const char* temp = mk_tmp_name();
    ForwardReferenceList forwardStmts(*receiver);
    forwardStmts.insertContainer(statement_VarDecl(defaultLoc, temp,
      qual_type_cons(NULL, typ_Pointer(pkind_PDataPointer(typ))),
      opt_some_container(init_expr_Single_init(expression_cons(
        copy_loc(defaultLoc), exp_node_Address(lvalueThisStar))))));

    if (hasInstanceContext()) {
      _tableForWaitingDeclarations.ensureGeneration(&record, unvisitedDecls());
    }
    else if (shouldDelay && !*shouldDelay)
      *shouldDelay = !_tableForWaitingDeclarations.hasVisited(&record);
    clang::RecordDecl::field_iterator fieldIterEnd = record.field_end();
    for (clang::RecordDecl::field_iterator fieldIter
        = record.field_begin(); fieldIter != fieldIterEnd; ++fieldIter) {
      clang::ImplicitValueInitExpr subExpr(fieldIter->getType());
      _implicitThisStar = expression_cons(copy_loc(defaultLoc),
          exp_node_FieldAccess(expression_cons(copy_loc(defaultLoc),
            exp_node_Dereference(expression_cons(copy_loc(defaultLoc),
                exp_node_Variable(variable_Local(qualified_name_cons(
                  NULL, strdup(temp))))))),
            copy_string(fieldIter->getNameAsString())));
      if(fieldIter->getType()->isReferenceType())
        _implicitThisStar =
          expression_cons(
            copy_loc(defaultLoc),
            exp_node_Unary_operator(uokind_UOCastDeref(),_implicitThisStar));
      exp_node init_res = makeExpression(&subExpr, shouldDelay, receiver);
      if (init_res) {
        statement initPart = statement_Expression(copy_loc(
          defaultLoc), expression_cons(copy_loc(defaultLoc),
            _implicitThisStar ? exp_node_Assign(_implicitThisStar,
                expression_cons(copy_loc(defaultLoc), init_res))
                          : init_res));
        forwardStmts.insertContainer(initPart);
      }
      else if (_implicitThisStar) {
        free_expression(_implicitThisStar);
        _implicitThisStar = NULL;
      };
    };
    return exp_node_Dereference(expression_cons(copy_loc(
      defaultLoc), exp_node_Variable(variable_Local(
        qualified_name_cons(NULL, strdup(temp))))));
  };
}

exp_node
FramacVisitor::makeImplicitValueInitExpression(
    const clang::ImplicitValueInitExpr* expr, bool* shouldDelay,
    /* statement */ list* receiver, FramaCIRGenAction::ExpressionGuard& guard,
    const clang::Type* type)
{ if (!type)
    type = expr->getType().getTypePtr();
  clang::Type::TypeClass typeClass=type->getTypeClass();
  if ((typeClass >= clang::Type::ConstantArray
        && typeClass <= clang::Type::DependentSizedArray)
      || typeClass == clang::Type::Record) {
    if (!receiver) {
      std::cerr << "Unsupported initialization:";
#if CLANG_VERSION_MAJOR >= 11
      expr->dump(llvm::errs(), *_context);
#else
      expr->dump(llvm::errs(), _context->getSourceManager());
#endif
      std::cerr << "\nAborting\n";
      exit(2);
    };
    expression lvalueThisStar = guard.releaseExpr();
    if (expr->getStmtClass() == clang::Stmt::InitListExprClass) {
      qual_type typ = makeDefaultType(expr->getExprLoc(), expr->getType());
      location loc = makeLocation(expr->getSourceRange());
      init_expr init = makeInitExpr(expr->getType(),expr);
      const char* temp = mk_tmp_name();
      exp_node res = exp_node_Temporary(temp, typ, init, false);
      return exp_node_Assign(lvalueThisStar,expression_cons(copy_loc(loc),res));
    }
    else if (expr->getStmtClass() == clang::Stmt::ImplicitValueInitExprClass) {
      if (typeClass == clang::Type::ConstantArray)
        return makeConstantArrayInitExpression((const clang::ConstantArrayType&)
            *type, expr, lvalueThisStar, shouldDelay, receiver);
      else if (typeClass == clang::Type::Record)
        return makeRecordInitExpression(*((const clang::RecordType&)
            *type).getDecl(), expr, lvalueThisStar, shouldDelay, receiver);
      else {
        // dynamic array => generate a loop like ConstantArray
      };
    }
    std::cerr << "Unsupported initialization:";
#if CLANG_VERSION_MAJOR >= 11
    expr->dump(llvm::errs(), *_context);
#else
    expr->dump(llvm::errs(), _context->getSourceManager());
#endif
    std::cerr << "\nAborting\n";
    exit(2);
  };
  switch (typeClass) {
    case clang::Type::Pointer: case clang::Type::MemberPointer:
      return guard.setAssignResult(
        exp_node_PointerCast(
          makeDefaultType(expr->getExprLoc(), expr->getType()),
          reference_or_pointer_kind_RPKPointer(),
          makeIntLiteral(makeLocation(expr->getSourceRange()),IINT,0)),
        expr);
    case clang::Type::Builtin:
      { auto builtinType = llvm::dyn_cast<const clang::BuiltinType>(type);
        assert(builtinType);
          //integral init is handled somewhere else
          if (!type->isIntegerType()) { 
            if (type->isRealFloatingType()) {
              location loc = _clangUtils->makeExprLocation(*expr);
              fkind k = _clangUtils->makeFloatConstantType(type);
              expression res = makeFloatZero(loc, k);
              free_location(loc);
              return guard.setAssignResult(res, expr);
            }
            if (builtinType->getKind() == clang::BuiltinType::NullPtr) {
              return guard.setAssignResult(
                exp_node_PointerCast(
                  qual_type_cons(
                    NULL,
                    typ_Pointer(
                      pkind_PDataPointer(qual_type_cons(NULL, typ_Void())))),
                  reference_or_pointer_kind_RPKPointer(),
                  makeIntLiteral(makeLocation(expr->getSourceRange()),IINT,0)),
                expr);
            }
            std::cerr
              << "Unsupported Builtin-type: "
              << builtinType->getName(_context->getPrintingPolicy()).str()
              << "\nAborting\n";
            //TODO: raise exception and try to continue with rest of file.
            exit(2);
          };
      };
      break;
    case clang::Type::Paren:
      assert(llvm::dyn_cast<const clang::ParenType>(type));
      return makeImplicitValueInitExpression(expr, shouldDelay, receiver, guard,
        static_cast<const clang::ParenType*>(type)->desugar().getTypePtr());
    case clang::Type::UnresolvedUsing:
      assert(llvm::dyn_cast<const clang::UnresolvedUsingType>(type));
      return makeImplicitValueInitExpression(expr, shouldDelay, receiver, guard,
        static_cast<const clang::UnresolvedUsingType*>(type)->desugar()
          .getTypePtr());
    case clang::Type::LValueReference:
    case clang::Type::RValueReference:
    case clang::Type::FunctionNoProto:
    case clang::Type::FunctionProto:
      std::cerr << "Unsupported default initialization with reference type \""
          << expr->getType().getAsString () << "\"\nAborting\n";
      exit(2);
    case clang::Type::SubstTemplateTypeParm:
      { assert(llvm::dyn_cast<const clang::SubstTemplateTypeParmType>(type));
        return makeImplicitValueInitExpression(expr, shouldDelay, receiver,
          guard, static_cast<const clang::SubstTemplateTypeParmType*>(type)
            ->getReplacementType().getTypePtr());
      };
    case clang::Type::Typedef:
      assert(llvm::dyn_cast<const clang::TypedefType>(type));
      return makeImplicitValueInitExpression(expr, shouldDelay, receiver, guard,
        static_cast<const clang::TypedefType*>(type)->desugar().getTypePtr());
    case clang::Type::Elaborated:
      assert(llvm::dyn_cast<const clang::ElaboratedType>(type));
      return makeImplicitValueInitExpression(expr, shouldDelay, receiver, guard,
        static_cast<const clang::ElaboratedType*>(type)->desugar()
          .getTypePtr());
    default:
      break;
  };
  ikind kind = _clangUtils->makeIntConstantType(type);
  return 
    guard.setAssignResult(
      exp_node_Constant(
        compilation_constant_IntCst(kind, ICLITERAL,0)),expr);
}

exp_node FramacVisitor::makeUnaryExprOrTypeTraitExpression(
    const clang::UnaryExprOrTypeTraitExpr* expr, bool* shouldDelay,
    /* statement */ list* receiver, FramaCIRGenAction::ExpressionGuard& guard)
{ tcckind kind;
  if (expr->getKind() == clang::UETT_SizeOf)
    kind = TCCSIZEOF;
  else if (expr->getKind() == clang::UETT_AlignOf)
    kind = TCCALIGNOF;
  else {
    std::cerr << "Unsupported expression:";
#if CLANG_VERSION_MAJOR >= 11
    expr->dump(llvm::errs(), *_context);
#else
    expr->dump(llvm::errs(), _context->getSourceManager());
#endif
    std::cerr << "\nAborting\n";
    exit(2);
  };
  typ typeArgument;
  if (hasInstanceContext()) {
    FramaCIRGenAction::UnvisitedRegistration
      unvisitedRegistration(*this);
    typeArgument =
      _clangUtils->makePlainType(
        expr->getExprLoc(), expr->getTypeOfArgument(), &unvisitedRegistration);
  }
  else
    typeArgument =
      _clangUtils->makePlainType(expr->getExprLoc(), expr->getTypeOfArgument());
  return 
    guard.setAssignResult(
      exp_node_Constant(compilation_constant_TypeCst(kind, typeArgument)), expr);
}

exp_node FramacVisitor::makeAtomicExprExpression(
    const clang::AtomicExpr* sexpr, bool* shouldDelay,
    /* statement */ list* receiver, FramaCIRGenAction::ExpressionGuard& guard) {
  list /* expression */ arguments = NULL;
  clang::AtomicExpr* expr = const_cast<clang::AtomicExpr*>(sexpr);
  ForwardReferenceList forwardArguments(arguments);
  int childNumber = expr->getNumSubExprs();
  list /* qual_type */ parameters = NULL;
  ForwardReferenceList forwardParameters(parameters);
  for (int child = 0; child < childNumber; ++child) {
    clang::Expr const* childExpr = expr->getSubExprs()[child];
    expression argument = makeLocExpression(childExpr, shouldDelay, receiver);
    forwardArguments.insertContainer(argument);
    FramaCIRGenAction::UnvisitedRegistration
      unvisitedRegistration(*this);
    forwardParameters.insertContainer(
      _clangUtils->makeType(
        childExpr->getExprLoc(),childExpr->getType(), &unvisitedRegistration));
  }

  const char* name = NULL;
  switch (expr->getOp()) {
#define BUILTIN(ID, TYPE, ATTRS)
#define ATOMIC_BUILTIN(ID, TYPE, ATTRS)                 \
    case clang::AtomicExpr::AO ## ID: name = #ID; break;
#include "clang/Basic/Builtins.def"
    default:
      break;
  };
  qualified_name callName = qualified_name_cons(NULL, strdup(name));
  signature sig = signature_cons(qual_type_cons(NULL, typ_Void()),
    parameters, false);
  return exp_node_Static_call(callName, sig, funkind_FKFunction(), arguments,
      tkind_TStandard(), true /* isExternC */);
}

exp_node FramacVisitor::makeExpression(
  const clang::Expr* expr, bool* shouldDelay, /* statement */ list* receiver) {
  bool hasImplicitThisStar = _implicitThisStar != NULL;
  bool isImplicitThisBare = _isImplicitThisBare;
  _isImplicitThisBare = false;
  FramaCIRGenAction::ExpressionGuard guard(*this, _implicitThisStar);

  switch (expr->getStmtClass()) {
    case clang::Stmt::ArraySubscriptExprClass:
      { assert(llvm::dyn_cast<clang::ArraySubscriptExpr>(expr));
        const clang::ArraySubscriptExpr* array
            = static_cast<const clang::ArraySubscriptExpr*>(expr);
        expression base = makeLocExpression(array->getBase(), shouldDelay);
        expression index = makeLocExpression(array->getIdx(), shouldDelay);
        return guard.setAssignResult(exp_node_ArrayAccess(base, index), expr);
      }
    case clang::Stmt::BinaryOperatorClass:
    case clang::Stmt::CompoundAssignOperatorClass:
      { assert(llvm::dyn_cast<clang::BinaryOperator>(expr));
        const clang::BinaryOperator* binaryOperator
          = static_cast<const clang::BinaryOperator*>(expr);
        bokind type = BOPLUS;
        assign_kind assignment = AKRVALUE;
        switch (binaryOperator->getOpcode()) {
          case clang::BO_PtrMemD:
          case clang::BO_PtrMemI:
            return makeExpression(binaryOperator->getRHS(), shouldDelay);
          case clang::BO_Mul:
            type = BOTIMES; assignment = AKRVALUE; break;
          case clang::BO_Div:
            type = BODIVIDE; assignment = AKRVALUE; break;
          case clang::BO_Rem:
            type = BOMODULO; assignment = AKRVALUE; break;
          case clang::BO_Add:
            type = BOPLUS; assignment = AKRVALUE; break;
          case clang::BO_Sub:
            type = BOMINUS; assignment = AKRVALUE; break;
          case clang::BO_Shl:
            type = BOLEFTSHIFT; assignment = AKRVALUE; break;
          case clang::BO_Shr:
            type = BORIGHTSHIFT; assignment = AKRVALUE; break;
          case clang::BO_LT:
            type = BOLESS; assignment = AKRVALUE; break;
          case clang::BO_GT:
            type = BOGREATER; assignment = AKRVALUE; break;
          case clang::BO_LE:
            type = BOLESSOREQUAL; assignment = AKRVALUE; break;
          case clang::BO_GE:
            type = BOGREATEROREQUAL; assignment = AKRVALUE; break;
          case clang::BO_EQ:
            type = BOEQUAL; assignment = AKRVALUE; break;
          case clang::BO_NE:
            type = BODIFFERENT; assignment = AKRVALUE; break;
          case clang::BO_And:
            type = BOBITAND; assignment = AKRVALUE; break;
          case clang::BO_Xor:
            type = BOBITEXCLUSIVEOR; assignment = AKRVALUE; break;
          case clang::BO_Or:
            type = BOBITOR; assignment = AKRVALUE; break;
          case clang::BO_LAnd:
            type = BOLOGICALAND; assignment = AKRVALUE; break;
          case clang::BO_LOr:
            type = BOLOGICALOR; assignment = AKRVALUE; break;
          case clang::BO_Assign:
            { exp_node lhsPart = makeExpression(binaryOperator->getLHS(),
                                                shouldDelay,receiver);
              expression elhsPart = expression_cons(makeLocation(
                    binaryOperator->getLHS()->getSourceRange()), lhsPart);
              exp_node rhsPart = makeExpression(binaryOperator->getRHS(),
                                                shouldDelay,receiver);
              expression erhsPart = expression_cons(makeLocation(
                    binaryOperator->getRHS()->getSourceRange()), rhsPart);
              return guard.setAssignResult(exp_node_Assign(elhsPart, erhsPart),
                  expr);
            };
          case clang::BO_MulAssign:
            type = BOTIMES; assignment = AKASSIGN; break;
          case clang::BO_DivAssign:
            type = BODIVIDE; assignment = AKASSIGN; break;
          case clang::BO_RemAssign:
            type = BOMODULO; assignment = AKASSIGN; break;
          case clang::BO_AddAssign:
            type = BOPLUS; assignment = AKASSIGN; break;
          case clang::BO_SubAssign:
            type = BOMINUS; assignment = AKASSIGN; break;
          case clang::BO_ShlAssign:
            type = BOLEFTSHIFT; assignment = AKASSIGN; break;
          case clang::BO_ShrAssign:
            type = BORIGHTSHIFT; assignment = AKASSIGN; break;
          case clang::BO_AndAssign:
            type = BOBITAND; assignment = AKASSIGN; break;
          case clang::BO_XorAssign:
            type = BOBITEXCLUSIVEOR; assignment = AKASSIGN; break;
          case clang::BO_OrAssign:
            type = BOBITOR; assignment = AKASSIGN; break;
          case clang::BO_Comma:
            type = BOCOMMA; assignment = AKRVALUE; break;
          case clang::BO_Cmp: // does not seem to be fully supported by clang
            //itself according to https://clang.llvm.org/cxx_status.html#cxx2a
          std::cerr << "Unsupported spaceship (<=>) operator:";
#if CLANG_VERSION_MAJOR >= 11
          expr->dump(llvm::errs(), *_context);
#else
          expr->dump(llvm::errs(), _context->getSourceManager());
#endif
          std::cerr << "\nAborting\n";
          exit(2);
          default:
            std::cerr << "Unsupported binary expression:";
#if CLANG_VERSION_MAJOR >= 11
            expr->dump(llvm::errs(), *_context);
#else
            expr->dump(llvm::errs(), _context->getSourceManager());
#endif
            std::cerr << "\nAborting\n";
            exit(2);
        };
        exp_node lhsPart = makeExpression(binaryOperator->getLHS(),
                                          shouldDelay,receiver);
        expression elhsPart = expression_cons(makeLocation(
              binaryOperator->getLHS()->getSourceRange()), lhsPart);
        exp_node rhsPart = makeExpression(binaryOperator->getRHS(),
                                          shouldDelay,receiver);
        expression erhsPart = expression_cons(makeLocation(
              binaryOperator->getRHS()->getSourceRange()), rhsPart);
        return guard.setAssignResult(exp_node_Binary_operator(type, assignment,
              elhsPart, erhsPart), expr);
      }
    case clang::Stmt::ConditionalOperatorClass:
      { assert(llvm::dyn_cast<clang::ConditionalOperator>(expr));
        const clang::ConditionalOperator* ternaryOperator
            = static_cast<const clang::ConditionalOperator*>(expr);
        exp_node condition = makeExpression(ternaryOperator->getCond(),
                                            shouldDelay,receiver);
        expression econdition = expression_cons(makeLocation(
              ternaryOperator->getCond()->getSourceRange()), condition);
        exp_node truePart = makeExpression(ternaryOperator->getTrueExpr(),
                                           shouldDelay,receiver);
        expression etruePart = expression_cons(makeLocation(
              ternaryOperator->getTrueExpr()->getSourceRange()), truePart);
        exp_node falsePart = makeExpression(ternaryOperator->getFalseExpr(),
                                            shouldDelay,receiver);
        expression efalsePart = expression_cons(makeLocation(
              ternaryOperator->getFalseExpr()->getSourceRange()), falsePart);
        return guard.setAssignResult(exp_node_Conditional(econdition,
              etruePart, efalsePart), expr);
      }
    case clang::Stmt::CXXBoolLiteralExprClass:
      { bool val = (static_cast<const clang::CXXBoolLiteralExpr*>(expr))
            ->getValue();
        compilation_constant cst =
          compilation_constant_IntCst(IBOOL, ICLITERAL, (int64_t)val);
        return guard.setAssignResult(exp_node_Constant(cst), expr);
      }
    case clang::Stmt::CXXTemporaryObjectExprClass:
      return makeTemporaryObjectExpression(
          static_cast<const clang::CXXTemporaryObjectExpr*>(expr),
          shouldDelay, receiver);
    case clang::Stmt::CXXConstructExprClass: {
      const auto constructor_expr =
        llvm::dyn_cast<clang::CXXConstructExpr>(expr);
      return makeConstructExpression(
        constructor_expr,
        shouldDelay, receiver, hasImplicitThisStar, isImplicitThisBare, guard);
    }
    case clang::Stmt::CXXDefaultArgExprClass:
      { assert(llvm::dyn_cast<clang::CXXDefaultArgExpr>(expr));
        const clang::CXXDefaultArgExpr* defaultArg
            = static_cast<const clang::CXXDefaultArgExpr*>(expr);
        return guard.setAssignResult(makeExpression(defaultArg->getExpr(),
          shouldDelay,receiver), expr);
      };
    case clang::Stmt::CXXDeleteExprClass:
      assert(llvm::dyn_cast<clang::CXXDeleteExpr>(expr));
      return guard.setAssignResult(makeDeleteExpression(
          static_cast<const clang::CXXDeleteExpr*>(expr),
          shouldDelay,receiver), expr);
    case clang::Stmt::CXXNewExprClass:
      assert(llvm::dyn_cast<clang::CXXNewExpr>(expr));
      return 
        guard.setAssignResult(
          makeNewExpression(
            static_cast<const clang::CXXNewExpr*>(expr), shouldDelay,receiver),
          expr);
    case clang::Stmt::GNUNullExprClass:
    case clang::Stmt::CXXNullPtrLiteralExprClass:
      { compilation_constant cst =
          compilation_constant_IntCst(IINT, ICLITERAL, 0);
        return guard.setAssignResult(exp_node_Constant(cst), expr);
      }
    case clang::Stmt::CXXThisExprClass:
      { variable thisVariable = variable_FunctionParameter("this");
        return guard.setAssignResult(exp_node_Variable(thisVariable), expr);
      }
    case clang::Stmt::CXXThrowExprClass:
      assert(llvm::dyn_cast<clang::CXXThrowExpr>(expr));
      { const clang::Expr* subExpr = static_cast<const clang::CXXThrowExpr*>
          (expr)->getSubExpr();
        return guard.setAssignResult(exp_node_Throw(subExpr
          ? opt_some_container(expression_cons(
              makeLocation(subExpr->getSourceRange()),
              makeExpression(subExpr, shouldDelay, receiver)))
          : opt_none()), expr);
      };
    case clang::Stmt::CXXTypeidExprClass:
      std::cerr << "Unsupported typeid:";
#if CLANG_VERSION_MAJOR >= 11
      expr->dump(llvm::errs(), *_context);
#else
      expr->dump(llvm::errs(), _context->getSourceManager());
#endif
      std::cerr << "\nAborting\n";
      exit(2);
    case clang::Stmt::CallExprClass:
    case clang::Stmt::CXXOperatorCallExprClass:
      assert(llvm::dyn_cast<clang::CallExpr>(expr));
      return makeCallExpression(static_cast<const clang::CallExpr*>(expr),
          shouldDelay, receiver, guard);
    case clang::Stmt::CXXMemberCallExprClass:
      assert(llvm::dyn_cast<clang::CXXMemberCallExpr>(expr));
      return makeMemberCallExpression(
          static_cast<const clang::CXXMemberCallExpr*>(expr),
          shouldDelay, receiver, guard);
    case clang::Stmt::CStyleCastExprClass:
    case clang::Stmt::CXXFunctionalCastExprClass:
    case clang::Stmt::CXXStaticCastExprClass:
    case clang::Stmt::CXXReinterpretCastExprClass:
    case clang::Stmt::CXXConstCastExprClass:
    case clang::Stmt::ImplicitCastExprClass:
      // TODO: Do not generate cast when clang make it a noop
      assert(llvm::dyn_cast<clang::CastExpr>(expr));
      return makeCastExpression(
          static_cast<const clang::CastExpr*>(expr),
          shouldDelay, receiver, guard);
    case clang::Stmt::CXXDynamicCastExprClass:
      assert(llvm::dyn_cast<clang::CXXDynamicCastExpr>(expr));
      return makeDynamicCastExpression(
          static_cast<const clang::CXXDynamicCastExpr*>(expr),
          shouldDelay, receiver, guard);
    case clang::Stmt::CharacterLiteralClass:
      { assert(llvm::dyn_cast<clang::CharacterLiteral>(expr));
        const clang::CharacterLiteral* litteralExpr
            = static_cast<const clang::CharacterLiteral*>(expr);
        ikind type = ICHAR;
        if (litteralExpr->getKind() != clang::CharacterLiteral::Ascii) {
          if (litteralExpr->getKind() == clang::CharacterLiteral::Wide)
            type = IWCHAR;
          else {
            std::cerr << "Unsupported character type:";
#if CLANG_VERSION_MAJOR >= 11
            expr->dump(llvm::errs(), *_context);
#else
            expr->dump(llvm::errs(), _context->getSourceManager());
#endif
            std::cerr << "\nAborting\n";
            exit(2);
          };
        };
        return
          guard.setAssignResult(
            exp_node_Constant(
              compilation_constant_IntCst(
                type, ICLITERAL, (int64_t) litteralExpr->getValue())),
            expr);
      };
    case clang::Stmt::CompoundLiteralExprClass:
      assert(llvm::dyn_cast<clang::CompoundLiteralExpr>(expr));
      return makeCompoundLiteralExpression(
          static_cast<const clang::CompoundLiteralExpr*>(expr),
          shouldDelay, receiver, hasImplicitThisStar, guard);
    case clang::Stmt::DeclRefExprClass:
      assert(llvm::dyn_cast<clang::DeclRefExpr>(expr));
      return makeDeclRefExpression(static_cast<const clang::DeclRefExpr*>
          (expr), shouldDelay, receiver, guard);
    case clang::Stmt::FloatingLiteralClass:
      { assert(llvm::dyn_cast<clang::FloatingLiteral>(expr));
        const clang::FloatingLiteral* floatingLitteral
            = static_cast<const clang::FloatingLiteral*>(expr);
        fkind k =
          _clangUtils->makeFloatConstantType(expr->getType().getTypePtr());
        location loc = _clangUtils->makeLocation(expr->getSourceRange());
        llvm::SmallVector<char, 10> result;
        floatingLitteral->getValue().toString(result, 16, 3);
        std::string strResult(result.data(), result.size());
        if (strResult.find_first_of(".EelLfF") == std::string::npos)
          strResult += ".0";
        return guard.setAssignResult(
          makeFloatConstant(loc, k, strResult.data()), expr);
      };
    case clang::Stmt::IntegerLiteralClass:
      { assert(llvm::dyn_cast<clang::IntegerLiteral>(expr));
        ikind kind =
          _clangUtils->makeIntConstantType(expr->getType().getTypePtr());
        const clang::IntegerLiteral* i
            = static_cast<const clang::IntegerLiteral*>(expr);
        return
          guard.setAssignResult(
            exp_node_Constant(
              compilation_constant_IntCst(
                kind, ICLITERAL,
                (int64_t) i->getValue().getLimitedValue(UINT64_MAX))),
            expr);
      };
    case clang::Stmt::MaterializeTemporaryExprClass:
      assert(llvm::dyn_cast<clang::MaterializeTemporaryExpr>(expr));
      return makeMaterializeTemporaryExpression(
          static_cast<const clang::MaterializeTemporaryExpr*>(expr),
          shouldDelay, receiver, guard);
    case clang::Stmt::CXXBindTemporaryExprClass:
      assert(llvm::dyn_cast<clang::CXXBindTemporaryExpr>(expr));
      return makeBindTemporaryExpression(
          static_cast<const clang::CXXBindTemporaryExpr*>(expr),
          shouldDelay, receiver, hasImplicitThisStar, guard);
    case clang::Stmt::MemberExprClass:
      { assert(llvm::dyn_cast<clang::MemberExpr>(expr));
        const clang::MemberExpr* fieldAccessExpr
            = static_cast<const clang::MemberExpr*>(expr);
        bool isReferenceField = _clangUtils->lvalHasRefType(expr);
        clang::ValueDecl* memberDecl = fieldAccessExpr->getMemberDecl();
        clang::Decl::Kind kind = memberDecl->getKind();
        if (kind >= clang::Decl::firstVar && kind <= clang::Decl::lastVar) {
          assert(llvm::dyn_cast<clang::VarDecl>(memberDecl));
          clang::VarDecl* fieldDecl = static_cast<clang::VarDecl*>(memberDecl);
          if (fieldDecl->isStaticDataMember()) {
            exp_node evar =
              exp_node_Variable(
                variable_Global(_clangUtils->makeQualifiedName(*fieldDecl)));
            if (isReferenceField) {
              expression var_expr =
                expression_cons(makeLocation(expr->getSourceRange()), evar);
              evar =
                exp_node_Unary_operator(uokind_UOCastDeref(), var_expr);
            }
            return guard.setAssignResult(evar,expr);
          }
        };
        const clang::CXXRecordDecl* base = fieldAccessExpr->getBase()
            ->getType().getTypePtr()->getPointeeCXXRecordDecl();
        if (!base)
          base = fieldAccessExpr->getBase()->getType().getTypePtr()
              ->getCanonicalTypeInternal().getTypePtr()->getAsCXXRecordDecl();
        if (base) {
          if (hasInstanceContext()) {
            _tableForWaitingDeclarations.ensureGeneration(
              base, unvisitedDecls());
          }
          else if (shouldDelay && !*shouldDelay)
            *shouldDelay = !_tableForWaitingDeclarations.hasVisited(base);
        };
        expression subnode =
          makeLocExpression(fieldAccessExpr->getBase(), shouldDelay,receiver);
        // isArrow distinguishes between x->f and x.f
        // we translate the former as (*x).f
        if (fieldAccessExpr->isArrow()) {
          location base_loc =
            makeLocation(fieldAccessExpr->getBase()->getSourceRange());
          subnode =
            expression_cons(base_loc, exp_node_Dereference(subnode));
        }
        const char* field_name =
          _clangUtils->get_field_name(fieldAccessExpr->getMemberDecl());
        exp_node field_acc = exp_node_FieldAccess(subnode,field_name);
        if (isReferenceField) {
          location loc = makeLocation(expr->getSourceRange());
          expression efield_acc = expression_cons(loc, field_acc);
          field_acc = exp_node_Unary_operator(uokind_UOCastDeref(),efield_acc);
        }
        return guard.setAssignResult(field_acc, expr);
      };
    case clang::Stmt::ParenExprClass:
      { assert(llvm::dyn_cast<clang::ParenExpr>(expr));
        const clang::ParenExpr* subExpr
            = static_cast<const clang::ParenExpr*>(expr);
        return guard.setAssignResult(makeExpression(subExpr->getSubExpr(),
            shouldDelay,receiver), expr);
      };
    case clang::Stmt::PredefinedExprClass:
      { // Only used for the __func__ identifier and its siblings
        // __FUNCTION__ and __PRETTY_FUNCTION__
        // we don't make a distinction between them.
        variable v = variable_Local(qualified_name_cons(NULL,"__func__"));
        return guard.setAssignResult( exp_node_Variable(v), expr);
      }
    case clang::Stmt::UnaryOperatorClass:
      assert(llvm::dyn_cast<clang::UnaryOperator>(expr));
      return makeUnaryOperator(static_cast<const clang::UnaryOperator*>(expr),
          shouldDelay, receiver, guard);
    case clang::Stmt::StringLiteralClass:
      { assert(llvm::dyn_cast<clang::StringLiteral>(expr));
        const clang::StringLiteral* string
          = static_cast<const clang::StringLiteral*>(expr);
        if (string->getCharByteWidth() == 1)
          return guard.setAssignResult(exp_node_String(strdup(string
                ->getString().str().c_str())), expr);
        else {
          std::cerr << "Warning: unsupported constant wide string"
            << std::endl;
          char* result = (char*) malloc(string->getByteLength()
              + string->getCharByteWidth());
          memcpy(result, string->getBytes().data(), string->getByteLength());
          memset(result + string->getByteLength(),0,string->getCharByteWidth());
          return guard.setAssignResult(exp_node_String(result), expr);
        };
      };
    case clang::Stmt::TypeTraitExprClass:
      { assert(llvm::dyn_cast<clang::TypeTraitExpr>(expr));
        const clang::TypeTraitExpr* booleanTraits
          = static_cast<const clang::TypeTraitExpr*>(expr);
        compilation_constant cst =
          compilation_constant_IntCst(IBOOL, ICSTATICCONST,
            booleanTraits->getValue());
        return guard.setAssignResult(exp_node_Constant(cst), expr);
      }
    case clang::Stmt::SubstNonTypeTemplateParmExprClass: {
      auto targ = static_cast<const clang::SubstNonTypeTemplateParmExpr*>(expr);
      exp_node e = makeExpression(targ->getReplacement(), shouldDelay);
      return guard.setAssignResult(e,expr);
    }
    case clang::Stmt::VAArgExprClass:
      { assert (llvm::dyn_cast<clang::VAArgExpr>(expr));
        const clang::VAArgExpr* va_arg
          = static_cast<const clang::VAArgExpr*>(expr);
        expression subexpr = makeLocExpression(va_arg->getSubExpr(),
            shouldDelay);
        clang::QualType va_arg_type = va_arg->getWrittenTypeInfo()->getType();
        qual_type exp_type = makeDefaultType(va_arg->getExprLoc(), va_arg_type);
        return guard.setAssignResult( exp_node_VAArg(subexpr, exp_type), expr);
      }
    case clang::Stmt::ImplicitValueInitExprClass:
      assert(llvm::dyn_cast<clang::ImplicitValueInitExpr>(expr));
      return makeImplicitValueInitExpression(
          static_cast<const clang::ImplicitValueInitExpr*>(expr),
          shouldDelay, receiver, guard);
    case clang::Stmt::UnaryExprOrTypeTraitExprClass:
      assert(llvm::dyn_cast<clang::UnaryExprOrTypeTraitExpr>(expr));
      return makeUnaryExprOrTypeTraitExpression(
          static_cast<const clang::UnaryExprOrTypeTraitExpr*>(expr),
          shouldDelay, receiver, guard);
    case clang::Stmt::AtomicExprClass:
      assert(llvm::dyn_cast<clang::AtomicExpr>(expr));
      { const clang::AtomicExpr* thisExpr
          = static_cast<const clang::AtomicExpr*>(expr);
        clang::IdentifierInfo* info = NULL;
        switch (thisExpr->getOp()) {
          case clang::AtomicExpr::AO__atomic_load:
            info = &_context->Idents.get("__atomic_load");
            break;
          case clang::AtomicExpr::AO__atomic_load_n:
            info = &_context->Idents.get("__atomic_load_n");
            break;
          case clang::AtomicExpr::AO__atomic_store:
            info = &_context->Idents.get("__atomic_store");
            break;
          case clang::AtomicExpr::AO__atomic_store_n:
            info = &_context->Idents.get("__atomic_store_n");
            break;
          case clang::AtomicExpr::AO__atomic_exchange:
            info = &_context->Idents.get("__atomic_exchange");
            break;
          case clang::AtomicExpr::AO__atomic_exchange_n:
            info = &_context->Idents.get("__atomic_exchange_n");
            break;
          case clang::AtomicExpr::AO__atomic_compare_exchange:
            info = &_context->Idents.get("__atomic_compare_exchange");
            break;
          case clang::AtomicExpr::AO__atomic_compare_exchange_n:
            info = &_context->Idents.get("__atomic_compare_exchange_n");
            break;
          case clang::AtomicExpr::AO__atomic_fetch_add:
            info = &_context->Idents.get("__atomic_fetch_add");
            break;
          case clang::AtomicExpr::AO__atomic_fetch_sub:
            info = &_context->Idents.get("__atomic_fetch_sub");
            break;
          case clang::AtomicExpr::AO__atomic_fetch_and:
            info = &_context->Idents.get("__atomic_fetch_and");
            break;
          case clang::AtomicExpr::AO__atomic_fetch_or:
            info = &_context->Idents.get("__atomic_fetch_or");
            break;
          case clang::AtomicExpr::AO__atomic_fetch_xor:
            info = &_context->Idents.get("__atomic_fetch_xor");
            break;
          case clang::AtomicExpr::AO__atomic_fetch_nand:
            info = &_context->Idents.get("__atomic_fetch_nand");
            break;
          case clang::AtomicExpr::AO__atomic_add_fetch:
            info = &_context->Idents.get("__atomic_add_fetch");
            break;
          case clang::AtomicExpr::AO__atomic_sub_fetch:
            info = &_context->Idents.get("__atomic_sub_fetch");
            break;
          case clang::AtomicExpr::AO__atomic_and_fetch:
            info = &_context->Idents.get("__atomic_and_fetch");
            break;
          case clang::AtomicExpr::AO__atomic_or_fetch:
            info = &_context->Idents.get("__atomic_or_fetch");
            break;
          case clang::AtomicExpr::AO__atomic_xor_fetch:
            info = &_context->Idents.get("__atomic_xor_fetch");
            break;
          case clang::AtomicExpr::AO__atomic_nand_fetch:
            info = &_context->Idents.get("__atomic_nand_fetch");
            break;
          default:
            std::cerr << "Unsupported atomic builtin:";
#if CLANG_VERSION_MAJOR >= 11
            expr->dump(llvm::errs(), *_context);
#else
            expr->dump(llvm::errs(), _context->getSourceManager());
#endif
            std::cerr << "\nAborting\n";
            exit(2);
        };
        clang::LookupResult R(*_sema, info, expr->getSourceRange().getBegin(),
               clang::Sema::LookupOrdinaryName);
        const clang::DeclContext* context = _context->getTranslationUnitDecl();
        if (_sema->LookupQualifiedName(R, const_cast<clang::DeclContext*>
            (context))) {
          if (R.getResultKind() == clang::LookupResult::Found) {
            assert(llvm::dyn_cast<clang::FunctionDecl>(
              R.getFoundDecl()));
            ensureBuiltinDeclaration(
              (clang::Builtin::ID) ((thisExpr->getOp()
                  - clang::AtomicExpr::AO__atomic_load)
                  + clang::Builtin::BI__atomic_load),
              static_cast<const clang::FunctionDecl*>(R.getFoundDecl()),
              true /* doesForceVariadic */);
          };
        };
      };
      return makeAtomicExprExpression(static_cast<const clang::AtomicExpr*>(
          expr), shouldDelay, receiver, guard);
    case clang::Stmt::ExprWithCleanupsClass:
      { assert (llvm::dyn_cast<clang::ExprWithCleanups>(expr));
        const clang::ExprWithCleanups* sexpr
          = static_cast<const clang::ExprWithCleanups*>(expr);
        _cleanups.push_back(sexpr);
        exp_node subnode = makeExpression(sexpr->getSubExpr(),
            shouldDelay, receiver);
        return guard.setAssignResult(subnode, expr);
      }
    case clang::Stmt::CXXScalarValueInitExprClass:
      { assert (llvm::dyn_cast<clang::CXXScalarValueInitExpr>(expr));
        const clang::CXXScalarValueInitExpr* sexpr
          = static_cast<const clang::CXXScalarValueInitExpr*>(expr);
        clang::QualType type = sexpr->getTypeSourceInfo()->getType();
        if (_clangUtils->isArithmeticType(type.getTypePtr())) {
          typ pltype = _clangUtils->makeArithmeticType(type.getTypePtr());
          if (pltype->tag_typ == INT) {
            ikind kind = pltype->cons_typ.Int.kind;
            free_typ(pltype);
            return
              guard.setAssignResult(exp_node_Constant(
                  compilation_constant_IntCst(
                    kind, ICLITERAL,
                    (int64_t) 0)),
                expr);
          }
          else if (pltype->tag_typ == FLOAT) {
            fkind kind = pltype->cons_typ.Float.kind;
            location loc = _clangUtils->makeLocation(expr->getSourceRange());
            expression zero = makeFloatZero(loc,kind);
            free_location(loc);
            free_typ(pltype);
            return guard.setAssignResult(zero,expr);
          }
          else
            { assert(false); }
        }
        else if (_clangUtils->isPointerType(type.getTypePtr())) {
          return guard.setAssignResult(
            makeNullPointer(
              _clangUtils->makeLocation(expr->getSourceRange()),
              _clangUtils->makeType(expr->getExprLoc(), type)),
            expr);
        }
        assert(false);
      };
    case clang::Stmt::CXXDefaultInitExprClass:
      { assert (llvm::dyn_cast<clang::CXXDefaultInitExpr>(expr));
        const clang::CXXDefaultInitExpr* sexpr
        = static_cast<const clang::CXXDefaultInitExpr*>(expr);
        if(sexpr->getExpr()->getStmtClass() == clang::Stmt::InitListExprClass) {
            _implicitThisStar = guard.releaseExpr();
            return  makeExpression(
                      sexpr->getExpr(),
                      shouldDelay,
                      receiver);
        }
        else {
          return  guard.setAssignResult(
                    makeExpression(
                      sexpr->getExpr(),
                      shouldDelay,
                      receiver),
                    expr);
        }
      };
    case clang::Stmt::InitListExprClass:
      { assert (llvm::dyn_cast<clang::InitListExpr>(expr));
        const clang::InitListExpr* sexpr
            = static_cast<const clang::InitListExpr*>(expr);
        assert(sexpr->getNumInits());
        switch(_memberType) {
          case clang::Type::ConstantArray:
            { expression field = guard.releaseExpr();
              location loc = makeLocation(expr->getSourceRange());
              exp_node all_init = NULL;
              for(unsigned i=0; i<sexpr->getNumInits(); ++i) {
                expression index = makeIntLiteral(loc,IULONG,i);
                if(!i) {
                  all_init =
                    exp_node_Assign(
                      expression_cons(
                        copy_loc(loc),
                        exp_node_ArrayAccess(
                          field,
                          index)),
                      expression_cons(
                        copy_loc(loc),
                        makeExpression(
                          sexpr->getInit(0),
                          shouldDelay,
                          receiver)));
                }
                else {
                  all_init =
                    exp_node_Binary_operator(
                      BOCOMMA,
                      AKRVALUE,
                      expression_cons(
                        copy_loc(loc),
                        all_init),
                      expression_cons(
                        copy_loc(loc),
                        exp_node_Assign(
                          expression_cons(
                            copy_loc(loc),
                            exp_node_ArrayAccess(
                              field,
                              index)),
                          expression_cons(
                            copy_loc(loc),
                            makeExpression(
                              sexpr->getInit(i),
                              shouldDelay,
                              receiver)))));
                }
              }
              free_location(loc);
              return all_init;
            }
          case clang::Type::Record:
            std::cerr <<
            "Unsupported type (record) for default init with braces"
            << std::endl;
            return NULL;
          default:
            return  guard.setAssignResult(
                      makeExpression(
                        sexpr->getInit(0),
                        shouldDelay,
                        receiver),
                      expr);
        }
      };
    case clang::Stmt::StmtExprClass:
      { assert (llvm::dyn_cast<clang::StmtExpr>(expr));
        const clang::StmtExpr* sexpr
            = static_cast<const clang::StmtExpr*>(expr);
        return exp_node_GnuBody(
                 cons_container(
                   makeStmt(
                     sexpr->getSubStmt(),
                     NULL,
                     _context->getTranslationUnitDecl(),
                     shouldDelay),
                     NULL));
      }
    case clang::Stmt::ShuffleVectorExprClass:
      { assert (llvm::dyn_cast<clang::ShuffleVectorExpr>(expr));
        const clang::ShuffleVectorExpr* sexpr
            = static_cast<const clang::ShuffleVectorExpr*>(expr);
        // [TODO] the semantics is not the correct one !
        return  guard.setAssignResult(
          makeExpression(sexpr->getExpr(0), shouldDelay, receiver), expr);
      }
    case clang::Stmt::ConvertVectorExprClass:
      { assert (llvm::dyn_cast<clang::ConvertVectorExpr>(expr));
        const clang::ConvertVectorExpr* sexpr
            = static_cast<const clang::ConvertVectorExpr*>(expr);
        // [TODO] the semantics is not the correct one !
        clang::QualType destinationType = sexpr->getTypeSourceInfo()
          ->getType();
        qual_type resultType =
          makeDefaultType(sexpr->getExprLoc(), destinationType);
        return guard.setAssignResult(
          exp_node_PointerCast(resultType,
            reference_or_pointer_kind_RPKPointer(),
            expression_cons(makeLocation(expr->getSourceRange()),
              makeExpression(sexpr->getSrcExpr(), shouldDelay, receiver))),
          expr);
      }
    case clang::Stmt::CXXStdInitializerListExprClass:
      {
        const clang::CXXStdInitializerListExpr* il =
          static_cast<const clang::CXXStdInitializerListExpr*>(expr);
        return guard.setAssignResult(make_initializer_list(il), expr);
      }
    case clang::Stmt::LambdaExprClass: {
      auto lam = static_cast<const clang::LambdaExpr*>(expr);
      return guard.setAssignResult(make_lambda_expr(lam), expr);
    }
    case clang::Stmt::AddrLabelExprClass:
    case clang::Stmt::DesignatedInitExprClass:
    case clang::Stmt::FunctionParmPackExprClass:
    case clang::Stmt::OpaqueValueExprClass:
    case clang::Stmt::ParenListExprClass:
    default: {}
  }
  std::cerr << "Unsupported expression:";
#if CLANG_VERSION_MAJOR >= 11
  expr->dump(llvm::errs(), *_context);
#else
  expr->dump(llvm::errs(), _context->getSourceManager());
#endif
  std::cerr << "\nAborting\n";
  exit(2);
}

compilation_constant FramacVisitor::makeConstantExpression(
  const clang::Expr* expr) const {
  clang::APValue result;
  if (expr->isCXX11ConstantExpr(*_context,&result)) {
    switch(result.getKind()) {
      // if the expression is a C++11 constant expression, the result
      // ought to be initialized by isCXX11ConstantExpr
#if CLANG_VERSION_MAJOR < 9
      case clang::APValue::Uninitialized: assert(false);
#else
      case clang::APValue::None:
      case clang::APValue::Indeterminate: assert(false);
#endif
      case clang::APValue::Int: {
        const llvm::APSInt& v = result.getInt();
        ikind kind =
          _clangUtils->makeIntConstantType(expr->getType().getTypePtr());
        return
          compilation_constant_IntCst(
            kind, ICLITERAL, v.getLimitedValue(UINT64_MAX));
      }
      case clang::APValue::Float: {
        const llvm::APFloat& v = result.getFloat();
        llvm::SmallVector<char,10> repr;
        v.toString(repr);
        fkind kind =
          _clangUtils->makeFloatConstantType(expr->getType().getTypePtr());
        return compilation_constant_FloatCst(kind, repr.data());
      }
      case clang::APValue::ComplexInt:
      case clang::APValue::ComplexFloat:
      case clang::APValue::LValue:
      case clang::APValue::Vector:
      case clang::APValue::Array:
      case clang::APValue::Struct:
      case clang::APValue::Union:
      case clang::APValue::MemberPointer:
      case clang::APValue::AddrLabelDiff:
#if CLANG_VERSION_MAJOR >= 9
      case clang::APValue::FixedPoint:
#endif
        {
          std::cerr << "unsupported constant expression: ";
          expr->dump();
          expr->dump();
          std::cerr << "\nAborting\n";
          exit(2);
        }
      default:
        break;
    }
  };
  std::cerr << "constant expression cannot be evaluated at compile-time";
  expr->dump();
  std::cerr << "\nAborting\n";
  exit(2);
}

cond_statement FramacVisitor::makeCondition(
  const clang::VarDecl* var_decl, expression e, bool* shouldDelay) {
  if (var_decl) {
    qual_type typ =
      makeDefaultType(
        var_decl->getLocation(), var_decl->getType());
    const char* name = copy_string(var_decl->getNameAsString());
    makeImplicitThis(var_decl);
    init_expr init =
      makeInitExpr(var_decl->getType(),var_decl->getInit(), shouldDelay);
    return cond_statement_CondVarDecl(name, typ, init);
  }
  else {
    return cond_statement_CondExpression(e);
  }
}

void FramacVisitor::makeImplicitThis(const clang::VarDecl* vdec) const {
  if (isRecordOrRecordRef(vdec->getType().getTypePtr()) ||
    _clangUtils->isConstantArrayType(vdec->getType().getTypePtr()))
  { location loc = makeLocation(vdec->getSourceRange());
    variable v = variable_Local(_clangUtils->makeQualifiedName(*vdec));
    expression var = expression_cons(loc,exp_node_Variable(v));
    _implicitThisStar = var;
  };
}

void FramacVisitor::readStatementCommentUntil(
    clang::SourceLocation sourceLocation, location& commentLocation,
    list& /*<statement>*/stmts, ForwardReferenceList& forwardStmts,
    const clang::Stmt* nextStmt, ForwardReferenceList* container,
    const clang::DeclContext* context) {
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  int previousTemplateComment = _templateCommentIndex,
      previousComment = _commentIndex;
  advanceCommentUntil(sourceLocation);

  while (previousTemplateComment < _templateCommentIndex) {
    ++previousTemplateComment;
    switch (_annotationCommentList[previousTemplateComment]->getKind()) {
      case AnnotationComment::KGlobal:
        parseGlobalComment(*_annotationCommentList[previousTemplateComment],
            context);
        break;
      case AnnotationComment::KGhost:
        parseGhostStatement(
          *_annotationCommentList[previousTemplateComment],
          context,
          container ? container : &forwardStmts);
        if (!container && stmts) {
          statement s = (statement) stmts->element.container;
          assert(s->tag_statement == GHOST_BLOCK);
          commentLocation = s->cons_statement.Ghost_block.loc;
        }
        break;

      case AnnotationComment::KLabel: // assert
        parseCodeAnnotation(*_annotationCommentList[previousTemplateComment],
            context, container ? container : &forwardStmts);
        if (!container && stmts) {
          assert(((statement) stmts->element.container)->tag_statement
              == CODE_ANNOT);
          commentLocation = ((statement) stmts->element.container)
            ->cons_statement.Code_annot.loc;
        };
        break;
      case AnnotationComment::KNextLoop:
        if (!nextStmt)
          parseDefaultComment(*_annotationCommentList[previousTemplateComment]);
        else {
          parseLoopAnnotation(*_annotationCommentList[previousTemplateComment],
              nextStmt, context, container ? container : &forwardStmts);
          if (!container && stmts) {
            assert(((statement) stmts->element.container)->tag_statement
                == CODE_ANNOT);
            commentLocation = ((statement) stmts->element.container)
              ->cons_statement.Code_annot.loc;
          };
        };
        break;
      case AnnotationComment::KNext:
      case AnnotationComment::KNextStatement:
        if (!nextStmt)
          parseDefaultComment(*_annotationCommentList[previousTemplateComment]);
        else {
          parseStatementAnnotation(
              *_annotationCommentList[previousTemplateComment],
              nextStmt, context, container ? container : &forwardStmts);
          if (!container && stmts) {
            assert(((statement) stmts->element.container)->tag_statement
                == CODE_ANNOT);
            commentLocation = ((statement) stmts->element.container)
              ->cons_statement.Code_annot.loc;
          };
        };
        break;
      case AnnotationComment::KOuterLoop:
        { std::cerr << "unimplemented next attachment" << std::endl;
          exit(2);
          assert(false);
          // Acsl::FunctionContract functionContract(Decl);
          // Acsl::Parser parser(Decl->getDeclContext(), _context, _sema,
          //    _parents.getScope(), functionContract);
          // parser.parse(*_annotationCommentList[previousTemplateComment]
          //    .getContent());
        };
        break;
      default:
        parseDefaultComment(*_annotationCommentList[previousTemplateComment]);
        break;
    };
  };

  while (previousComment < _commentIndex) {
    ++previousComment;
    switch (_annotationCommentList[previousComment]->getKind()) {
      case AnnotationComment::KGlobal:
        parseGlobalComment(*_annotationCommentList[previousComment], context);
        break;
      case AnnotationComment::KGhost:
        parseGhostStatement(
          *_annotationCommentList[previousComment],
          context,
          container ? container : &forwardStmts);
        if (!container && stmts) {
          statement s = (statement) stmts->element.container;
          assert (s->tag_statement == GHOST_BLOCK);
          commentLocation = s->cons_statement.Ghost_block.loc;
        }
        break;
      case AnnotationComment::KLabel:
        parseCodeAnnotation(*_annotationCommentList[previousComment], context,
            container ? container : &forwardStmts);
        break;
      case AnnotationComment::KNextLoop:
        if (!nextStmt)
          parseDefaultComment(*_annotationCommentList[previousComment]);
        else {
          parseLoopAnnotation(*_annotationCommentList[previousComment],
              nextStmt, context, container ? container : &forwardStmts);
          if (!container && stmts) {
            assert(((statement) stmts->element.container)->tag_statement
                == CODE_ANNOT);
            commentLocation = ((statement) stmts->element.container)
              ->cons_statement.Code_annot.loc;
          };
        };
        break;
      case AnnotationComment::KNext:
      case AnnotationComment::KNextStatement:
        if (!nextStmt)
          parseDefaultComment(*_annotationCommentList[previousComment]);
        else {
          parseStatementAnnotation(
              *_annotationCommentList[previousComment],
              nextStmt, context, container ? container : &forwardStmts);
          if (!container && stmts) {
            assert(((statement) stmts->element.container)->tag_statement
                == CODE_ANNOT);
            commentLocation = ((statement) stmts->element.container)
              ->cons_statement.Code_annot.loc;
          };
        };
        break;
      case AnnotationComment::KOuterLoop:
      default:
        parseDefaultComment(*_annotationCommentList[previousComment]);
        break;
    };
  };
}

statement FramacVisitor::makeExprWithCleanupsStmt(
    const clang::ExprWithCleanups* cleanups, location l,
    list& /*<statement>*/stmts, ForwardReferenceList& forwardStmts,
    const clang::DeclContext* context, bool* shouldDelay) {
  unsigned numDecls = cleanups->getNumObjects();
  const clang::Expr* mainExpr = cleanups->getSubExpr();
  _parents.pushBlock();
  // /* statement */ list* cursor = forwardStmts.getBack()
  //   ? &forwardStmts.getBack()->next : &forwardStmts.getFront();
  statement body = makeStmt(mainExpr,&forwardStmts, context, shouldDelay);
  forwardStmts.insertContainer(body);
  forwardStmts.advanceToEnd();
  for (unsigned declIndex = 0; declIndex < numDecls; ++declIndex) {
#if CLANG_VERSION_MAJOR >= 11
    clang::BlockDecl* decl = cleanups->getObject(declIndex).get<clang::BlockDecl*>();
#else
    clang::BlockDecl* decl = cleanups->getObject(declIndex);
#endif
    statement body = makeStmt(decl->getBody(), &forwardStmts, context,
        shouldDelay);
    forwardStmts.insertContainer(body);
  };
  _parents.popBlock();
  return statement_Block(l, stmts);
}

void FramacVisitor::makeCleanupsStmts(
    ForwardReferenceList& forwardStmts, /* stmt */ list* cursor,
    expression expr, const clang::DeclContext* context, bool* shouldDelay) {
  bool isLast = !forwardStmts.getBack()
    || cursor == &forwardStmts.getBack()->next;
  if (isLast)
    forwardStmts.advanceToEnd();
  std::vector<const clang::ExprWithCleanups*>::const_iterator
      iterEnd = _cleanups.end(), iter = _cleanups.begin();
  for (; iter != iterEnd; ++iter) {
    const clang::ExprWithCleanups& cleanup = **iter;
    unsigned numDecls = cleanup.getNumObjects();
    for (unsigned declIndex = 0; declIndex < numDecls; ++declIndex) {
#if CLANG_VERSION_MAJOR >= 11
      clang::BlockDecl* decl = cleanup.getObject(declIndex).get<clang::BlockDecl*>();
#else
      clang::BlockDecl* decl = cleanup.getObject(declIndex);
#endif
      statement body = makeStmt(decl->getBody(), &forwardStmts, context,
          shouldDelay);
      forwardStmts.insertContainer(body);
    };
  };
  _cleanups.clear();
}

void FramacVisitor::makeCleanupsStmts(
    ForwardReferenceList& forwardStmts, /* stmt */ list* cursor,
    init_expr iexpr, const clang::DeclContext* context, bool* shouldDelay) {
  bool isLast = !forwardStmts.getBack()
    || cursor == &forwardStmts.getBack()->next;
  if (isLast)
    forwardStmts.advanceToEnd();
  std::vector<const clang::ExprWithCleanups*>::const_iterator
      iterEnd = _cleanups.end(), iter = _cleanups.begin();
  for (; iter != iterEnd; ++iter) {
    const clang::ExprWithCleanups& cleanup = **iter;
    unsigned numDecls = cleanup.getNumObjects();
    for (unsigned declIndex = 0; declIndex < numDecls; ++declIndex) {
#if CLANG_VERSION_MAJOR >= 11
      clang::BlockDecl* decl = cleanup.getObject(declIndex).get<clang::BlockDecl*>();
#else
      clang::BlockDecl* decl = cleanup.getObject(declIndex);
#endif
      statement body = makeStmt(decl->getBody(), &forwardStmts, context,
          shouldDelay);
      forwardStmts.insertContainer(body);
    };
  };
  _cleanups.clear();
}

void FramacVisitor::makeCleanupsStmts(
    ForwardReferenceList& forwardStmts, /* stmt */ list* cursor,
    /* expression */ list exprs, const clang::DeclContext* context,
    bool* shouldDelay) {
  // list exprCursor = exprs;
  bool isLast = !forwardStmts.getBack()
    || cursor == &forwardStmts.getBack()->next;
  if (isLast)
    forwardStmts.advanceToEnd();
  std::vector<const clang::ExprWithCleanups*>::const_iterator
      iterEnd = _cleanups.end(), iter = _cleanups.begin();
  for (; iter != iterEnd; ++iter) {
    const clang::ExprWithCleanups& cleanup = **iter;
    unsigned numDecls = cleanup.getNumObjects();
    for (unsigned declIndex = 0; declIndex < numDecls; ++declIndex) {
#if CLANG_VERSION_MAJOR >= 11
      clang::BlockDecl* decl = cleanup.getObject(declIndex).get<clang::BlockDecl*>();
#else
      clang::BlockDecl* decl = cleanup.getObject(declIndex);
#endif
      statement body = makeStmt(decl->getBody(), &forwardStmts, context,
          shouldDelay);
      forwardStmts.insertContainer(body);
    };
  };
  _cleanups.clear();
}

statement FramacVisitor::makeCompoundStmt(
  const clang::CompoundStmt* block,
  location l, location commentLocation, list& /*<statement>*/stmts,
  ForwardReferenceList* container, ForwardReferenceList& forwardStmts,
  const clang::DeclContext* context, bool* shouldDelay)
{ _parents.pushBlock();
  addLabelsInSema(block);
  clang::CompoundStmt::const_body_iterator it = block->body_begin();
  clang::CompoundStmt::const_body_iterator last = block -> body_end();
  while(it < last) {
    statement stmt = makeStmt(*it, &forwardStmts, context, shouldDelay);
    forwardStmts.insertContainer(stmt);
    it++;
  }
  _parents.popBlock();
  readStatementCommentUntil(block->getSourceRange().getEnd(),
    commentLocation, stmts, forwardStmts, NULL, container, context);
  return statement_Block(l, stmts);
}

statement FramacVisitor::makeReturnStmt(
  const clang::ReturnStmt* returnStmt,
  location l, location commentLocation, list& /*<statement>*/stmts,
  ForwardReferenceList& forwardStmts, const clang::DeclContext* context,
  bool* shouldDelay) {
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  option e = opt_none();
  expression result = NULL;
  const clang::VarDecl* v = returnStmt->getNRVOCandidate();
  if (v && v->isNRVOVariable()) {
    // The returned expression should be a CXXConstructorExpr, which
    // is not marked as elidable since clang 13.0. Hence, do the elision here
    const clang::CXXConstructExpr* ce =
        llvm::dyn_cast<clang::CXXConstructExpr>(returnStmt->getRetValue());
    assert(ce && ce->getNumArgs() == 1);
    const clang::Expr *re = ce->getArg(0);
    result = makeLocExpression(re,shouldDelay);
  }
  else {
    const clang::Expr* re = returnStmt->getRetValue();
    if (re) result = makeLocExpression(re, shouldDelay);
  }
  if (result) e = opt_some_container(result);
  if (stmts || !_cleanups.empty()) {
    /* statement */ list* cursor = forwardStmts.getBack()
      ? &forwardStmts.getBack()->next : &forwardStmts.getFront();
    forwardStmts.insertContainer(statement_Return(l,e));
    if (!_cleanups.empty()) {
      assert(result);
      makeCleanupsStmts(forwardStmts, cursor, result, context, shouldDelay);
    };
    if (commentLocation) {
      commentLocation = copy_loc(commentLocation);
      extend_location_with(commentLocation, l);
    }
    else
      commentLocation = copy_loc(l);
  };
  return !stmts ? statement_Return(l,e)
                : statement_Block(commentLocation, stmts);
}

statement FramacVisitor::makeDeclStmt(
  const clang::DeclStmt* declStmt, location l, location commentLocation,
  list& /*<statement>*/stmts, ForwardReferenceList* container,
  ForwardReferenceList& forwardStmts,
  const clang::DeclContext* context, bool* shouldDelay) {
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  if (declStmt->isSingleDecl()) {
    clang::Decl* singleDecl=const_cast<clang::Decl*>(declStmt->getSingleDecl());
    _parents.addDecl(singleDecl);
    clang::Decl::Kind kindDecl = singleDecl->getKind();
    if (kindDecl >= clang::Decl::firstVar && kindDecl <= clang::Decl::lastVar) {
      assert(llvm::dyn_cast<clang::VarDecl>(singleDecl));
      const clang::VarDecl* varDecl
          = static_cast<const clang::VarDecl*>(singleDecl);
      qual_type type =
        makeDefaultType(varDecl->getLocation(), varDecl->getType());
      if (varDecl->isStaticLocal()) {
        type->qualifier = cons_plain(STATIC,type->qualifier);
      }
      const char* name = strdup(varDecl->getName().str().c_str());
      if (stmts) {
        commentLocation = commentLocation
          ? copy_loc(commentLocation) : copy_loc(l);
        extend_location_with(commentLocation, l);
      };
      if (varDecl->hasInit()) {
        makeImplicitThis(varDecl);
        const clang::ConstantArrayType* constantArrayType
            = _clangUtils->isConstantArrayType(varDecl->getType().getTypePtr());
        if (!constantArrayType) {
          option /*init_expr*/ init =
            opt_some_container(
              makeInitExpr(
                varDecl->getType(), varDecl->getInit(), shouldDelay));
          bool shouldGenerateBlock = false;
          if (stmts || !_cleanups.empty()) {
            /* statement */ list* containerCursor = NULL;
            if (container) {
              container->append(forwardStmts);
              containerCursor = container->getBack() ?
                &container->getBack()->next : &container->getFront();
              stmts = NULL;
              forwardStmts = ForwardReferenceList(stmts);
              shouldGenerateBlock = true;
            };
            /* statement */ list* cursor = forwardStmts.getBack()
              ? &forwardStmts.getBack()->next : &forwardStmts.getFront();
            (container ? *container : forwardStmts).insertContainer(
                statement_VarDecl(l,name, type, init));
            if (!_cleanups.empty()) {
              makeCleanupsStmts(forwardStmts, cursor,
                (init_expr) init->content.container, context, shouldDelay);
              if (container) {
                assert(containerCursor);
                while (stmts && ((statement) stmts->element.container)
                    ->tag_statement == VARDECL) {
                  statement current = (statement) stmts->element.container;
                  /* statement */ list next = *containerCursor;
                  *containerCursor = cons_container(current, next);
                  /* statement */ list old = stmts;
                  stmts = stmts->next;
                  free(old);
                };
                container->advanceToEnd();
              };
            };
            if ((stmts || shouldGenerateBlock) && !commentLocation)
              commentLocation = copy_loc(l);
          };
          return (!stmts && !shouldGenerateBlock)
              ? statement_VarDecl(l, name, type, init)
              : ((stmts)
                 ? statement_Block(commentLocation, stmts)
                 : statement_Nop(commentLocation)
                 );
        }
        else {
          statement resultDecl = statement_VarDecl(l, name, type, opt_none());
          if (container)
            container->insertContainer(resultDecl);
          else
            forwardStmts.insertContainer(resultDecl);
          expression lvalueThisStar = _implicitThisStar;
          _implicitThisStar = NULL;
          _isImplicitThisBare = false;
          /* statement */ list localStmts = NULL;
          exp_node tmpNode = makeConstantArrayInitExpression(*constantArrayType,
            varDecl->getInit(), lvalueThisStar, shouldDelay, &localStmts);
          free_exp_node(tmpNode);
          if (localStmts) {
            ForwardReferenceList local(localStmts);
            forwardStmts.append(local);
            if (!commentLocation)
              commentLocation = copy_loc(l);
          };
          return statement_Block(commentLocation, stmts);
        };
      };
      if (stmts) {
        forwardStmts.insertContainer(statement_VarDecl(l,name,type,opt_none()));
        free_location(commentLocation);
      };
      return !stmts ? statement_VarDecl(l,name,type,opt_none())
                    : statement_Block(commentLocation, stmts);
    };
  };

  clang::DeclStmt::const_decl_iterator iter_end = declStmt->decl_end();
  // the very first statement can reuse the location l directly. Afterwards,
  // we need to perform copies to avoid sharing.
  bool isAfterFirst = false;
  for (clang::DeclStmt::const_decl_iterator iter = declStmt->decl_begin();
      iter != iter_end; ++iter) {
    if (!isAfterFirst)
      isAfterFirst = true;
    else
      l = copy_loc(l);
    clang::Decl* singleDecl = *iter;
    _parents.addDecl(singleDecl);
    clang::Decl::Kind kindDecl = singleDecl->getKind();
    if (kindDecl >= clang::Decl::firstVar && kindDecl <= clang::Decl::lastVar) {
      assert(llvm::dyn_cast<clang::VarDecl>(singleDecl));
      const clang::VarDecl* varDecl
          = static_cast<const clang::VarDecl*>(singleDecl);
      qual_type type =
        makeDefaultType(varDecl->getLocation(), varDecl->getType());
      const char* name = strdup(varDecl->getName().str().c_str());
      option init = opt_none();
      if (varDecl->hasInit()) {
        makeImplicitThis(varDecl);
        init =
          opt_some_container(
            makeInitExpr(varDecl->getType(), varDecl->getInit(), shouldDelay));
      };
      if (container) {
        /* statement */ list* cursor = container->getBack()
          ? &container->getBack()->next : &container->getFront();
        container->insertContainer(statement_VarDecl(l,name,type,init));
        if (!_cleanups.empty())
          makeCleanupsStmts(*container, cursor,
            (init_expr) init->content.container, context, shouldDelay);
      }
      else {
        /* statement */ list* cursor = forwardStmts.getBack()
          ? &forwardStmts.getBack()->next : &forwardStmts.getFront();
        forwardStmts.insertContainer(statement_VarDecl(l,name,type,init));
        if (!_cleanups.empty())
          makeCleanupsStmts(forwardStmts, cursor,
            (init_expr) init->content.container, context, shouldDelay);
      };
    };
  };
  if (!container)
    return statement_Block(l, stmts);

  if (stmts) {
    if (commentLocation) {
      commentLocation = copy_loc(commentLocation);
      extend_location_with(commentLocation, l);
      free_location(l);
    }
    else
      commentLocation = l;
  };
  return !stmts ? statement_Nop(isAfterFirst ? copy_loc(l) : l)
                : statement_Block(commentLocation, stmts);
}

statement FramacVisitor::makeDoStmt(
  const clang::DoStmt* doStmt, location l,
  location commentLocation, list& /*<statement>*/stmts,
  ForwardReferenceList& forwardStmts, const clang::DeclContext* context,
  bool* shouldDelay) {
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  exp_node condition = makeExpression(doStmt->getCond(), shouldDelay);
  const char* conditionVarname = NULL;
  /* statement */ list* cursor = NULL;
  if (!_cleanups.empty()) {
    cursor = forwardStmts.getBack()
      ? &forwardStmts.getBack()->next : &forwardStmts.getFront();
    conditionVarname = mk_tmp_name ();
    forwardStmts.insertContainer(statement_VarDecl(copy_loc(l),
        conditionVarname, qual_type_cons(NULL, typ_Int(IBOOL)), opt_none()));
  };
  expression econdition = expression_cons(makeLocation(
      doStmt->getCond()->getSourceRange()), condition);
  statement body = makeStmt(doStmt->getBody(),NULL, context, shouldDelay);
  if (!_cleanups.empty()) {
    if (body->tag_statement != BLOCK)
      body = statement_Block(copy_loc(l), cons_container(body, NULL));
    ForwardReferenceList block(body->cons_statement.Block.instructions);
    block.insertContainer(statement_Expression(copy_loc(econdition->eloc),
      expression_cons(copy_loc(econdition->eloc), exp_node_Assign(
          expression_cons(copy_loc(econdition->eloc), exp_node_Variable(
              variable_Local(qualified_name_cons(NULL,
                strdup(conditionVarname))))),
          econdition))));
    makeCleanupsStmts(block, cursor, econdition, context, shouldDelay);
    econdition = expression_cons(copy_loc(econdition->eloc),
      exp_node_Variable(variable_Local(qualified_name_cons(NULL,
        strdup(conditionVarname)))));
  };
  statement result = statement_DoWhile(l,econdition,body,NULL);
  _delayedLoopAnnotation.attachTo(doStmt, result);
  if (stmts) {
    forwardStmts.insertContainer(result);
    if (commentLocation) {
      commentLocation = copy_loc(commentLocation);
      extend_location_with(commentLocation, l);
    }
    else
      commentLocation = copy_loc(l);
  };
  return !stmts ? result : statement_Block(commentLocation, stmts);
}

statement FramacVisitor::makeForStmt(
  const clang::ForStmt* forStmt, location l, location commentLocation,
  list& /*<statement>*/stmts, ForwardReferenceList& forwardStmts,
  const clang::DeclContext* context, bool* shouldDelay) {
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  init_statement init = NULL;
  if (!forStmt->getInit())
    init = init_statement_INop();
  else {
    clang::Stmt::StmtClass initClass = forStmt->getInit()->getStmtClass();
    if (initClass == clang::Stmt::DeclStmtClass) {
      assert(llvm::dyn_cast<clang::DeclStmt>(forStmt->getInit()));
      const clang::DeclStmt* declStmt
        = static_cast<const clang::DeclStmt*>(forStmt->getInit());
      list init_declarator_list = NULL;
      qual_type type;
      for(const clang::Decl* singleDecl : declStmt->getDeclGroup())
        {
          assert(llvm::dyn_cast<clang::VarDecl>(singleDecl));
          const clang::VarDecl* varDecl
            = static_cast<const clang::VarDecl*>(singleDecl);
          type
            = makeDefaultType(varDecl->getLocation(), varDecl->getType());
          const char* name = strdup(varDecl->getName().str().c_str());
          if (_implicitThisStar) {
            free_expression(_implicitThisStar);
            _implicitThisStar = NULL;
            _isImplicitThisBare = false;
          };
          if (type->plain_type->tag_typ == STRUCT) {
            location loc = makeLocation(varDecl->getSourceRange());
            variable v
              = variable_Local(_clangUtils->makeQualifiedName(*varDecl));
            expression var = expression_cons(loc,exp_node_Variable(v));
            _implicitThisStar = var;
          };
          /*init_expr*/ option ie = opt_none();
          if (varDecl->hasInit())
            ie =
              opt_some_container(
                makeInitExpr(
                  varDecl->getType(), varDecl->getInit(), shouldDelay));

          if (!_cleanups.empty()) {
            std::cerr << "Unsupported statements to clean the expression\n";
#if CLANG_VERSION_MAJOR >= 11
            forStmt->dump(llvm::errs(), *_context);
#else
            forStmt->dump(llvm::errs(), _context->getSourceManager());
#endif
            std::cerr << "\ncontinue the translation\n";
            _cleanups.clear();
          };
          init_declarator_list = cons_container(
            init_declarator_cons(
              name,
              type,
              ie),
            init_declarator_list);
        }
      init = init_statement_IVarDecl(
        init_declarator_list);
    }
    else if (initClass >= clang::Stmt::firstExprConstant
             && initClass <= clang::Stmt::lastExprConstant) {
      assert(llvm::dyn_cast<clang::Expr>(forStmt->getInit()));
      exp_node initExpr = makeExpression(static_cast<const clang::Expr*>(
                                           forStmt->getInit()), shouldDelay);
      if (!_cleanups.empty()) {
        std::cerr << "Unsupported statements to clean the expression\n";
#if CLANG_VERSION_MAJOR >= 11
        forStmt->dump(llvm::errs(), *_context);
#else
        forStmt->dump(llvm::errs(), _context->getSourceManager());
#endif
        std::cerr << "\ncontinue the translation\n";
        _cleanups.clear();
      };
      expression einitExpr = expression_cons(makeLocation(
                                               forStmt->getInit()->getSourceRange()), initExpr);
      init = init_statement_IExpression(einitExpr);
    }
  };
  /*expression*/ option econdition = NULL;
  if (forStmt->getCond()) {
    exp_node condition = makeExpression(forStmt->getCond(), shouldDelay);
    econdition = opt_some_container
      (expression_cons(makeLocation(forStmt->getCond()->getSourceRange()), condition));
    if (!_cleanups.empty()) {
      std::cerr << "Unsupported statements to clean the expression\n";
#if CLANG_VERSION_MAJOR >= 11
      forStmt->dump(llvm::errs(), *_context);
#else
      forStmt->dump(llvm::errs(), _context->getSourceManager());
#endif
      std::cerr << "\ncontinue the translation\n";
      _cleanups.clear();
    };
  }
  else {
    econdition = opt_none();
  };
  statement body = makeStmt(forStmt->getBody(), NULL, context, shouldDelay);
  incr_statement increment = NULL;
  if (forStmt->getInc()) {
    exp_node incrementExpr = makeExpression(forStmt->getInc(), shouldDelay);
    expression eincrementExpr = expression_cons(makeLocation(
                                                  forStmt->getInc()->getSourceRange()), incrementExpr);
    increment = incr_statement_CExpression(eincrementExpr);
    if (!_cleanups.empty()) {
      if (body->tag_statement != BLOCK)
        body = statement_Block(copy_loc(l), cons_container(body, NULL));
      ForwardReferenceList block(body->cons_statement.Block.instructions);
      /* statement */ list* cursor = block.getBack()
        ? &block.getBack()->next : &block.getFront();
      makeCleanupsStmts(block, cursor, eincrementExpr, context, shouldDelay);
    };
  }
  else
    increment = incr_statement_CNop();
  statement result = statement_For(l,init,econdition,increment,body,NULL);
  _delayedLoopAnnotation.attachTo(forStmt, result);
  if (stmts) {
    forwardStmts.insertContainer(result);
    if (commentLocation) {
      commentLocation = copy_loc(commentLocation);
      extend_location_with(commentLocation, l);
    }
    else
      commentLocation = copy_loc(l);
  };
  return !stmts ? result
    : statement_Block(commentLocation, stmts);
}

statement FramacVisitor::makeLabelStmt(
  const clang::LabelStmt* labelStmt, location l, location commentLocation,
  list& /*<statement>*/stmts, ForwardReferenceList* container,
  ForwardReferenceList& forwardStmts, const clang::DeclContext* context,
  bool* shouldDelay) {
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  if (container)
    container->insertContainer(statement_Label(l,labelStmt->getName()));
  else
    forwardStmts.insertContainer(statement_Label(l,labelStmt->getName()));
  statement subStatement = makeStmt(labelStmt->getSubStmt(),
      container ? container : &forwardStmts, context, shouldDelay);
  if (!container) {
    forwardStmts.insertContainer(subStatement);
    return statement_Block(l, stmts);
  };
  if (stmts) {
    forwardStmts.insertContainer(subStatement);
    if (commentLocation) {
      commentLocation = copy_loc(commentLocation);
      extend_location_with(commentLocation, l);
      free_location(l);
    }
    else
      commentLocation = l;
  };
  return !stmts ? subStatement : statement_Block(commentLocation, stmts);
}

statement FramacVisitor::makeIfStmt(
  const clang::IfStmt* ifStmt, location l, location commentLocation,
  list& /*<statement>*/stmts, ForwardReferenceList& forwardStmts,
  const clang::DeclContext* context, bool* shouldDelay) {
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  exp_node condition = makeExpression(ifStmt->getCond(), shouldDelay);
  expression econdition = expression_cons(makeLocation(
      ifStmt->getCond()->getSourceRange()), condition);

  if (!_cleanups.empty()) {
    const char* conditionVarname = mk_tmp_name ();
    forwardStmts.insertContainer(statement_VarDecl(copy_loc(l),
        conditionVarname, qual_type_cons(NULL, typ_Int(IBOOL)), opt_none()));
    /* statement */ list* cursor = forwardStmts.getBack()
      ? &forwardStmts.getBack()->next : &forwardStmts.getFront();
    forwardStmts.insertContainer(statement_Expression(
      copy_loc(econdition->eloc), expression_cons(copy_loc(econdition->eloc),
        exp_node_Assign(
          expression_cons(copy_loc(econdition->eloc), exp_node_Variable(
              variable_Local(qualified_name_cons(NULL,
                strdup(conditionVarname))))),
          econdition))));
    makeCleanupsStmts(forwardStmts, cursor, econdition, context, shouldDelay);
    econdition = expression_cons(copy_loc(econdition->eloc),
      exp_node_Variable(variable_Local(qualified_name_cons(NULL,
        strdup(conditionVarname)))));
  };

  const clang::VarDecl* declared_var = ifStmt->getConditionVariable();
  cond_statement cond = makeCondition(declared_var, econdition,
      shouldDelay);
  statement thenStmt = makeStmt(ifStmt->getThen(), NULL, context,
      shouldDelay);
  const clang::Stmt* cppElseStmt = ifStmt->getElse();
  statement elseStmt = cppElseStmt
    ? makeStmt(ifStmt->getElse(), NULL, context, shouldDelay)
    : statement_Nop(copy_loc(l));
  statement result = statement_Condition(l,cond,thenStmt,elseStmt);
  if (stmts) {
    forwardStmts.insertContainer(result);
    if (commentLocation) {
      commentLocation = copy_loc(commentLocation);
      extend_location_with(commentLocation, l);
    }
    else
      commentLocation = copy_loc(l);
  };
  return !stmts ? result : statement_Block(commentLocation, stmts);
}

statement FramacVisitor::makeSwitchStmt(
  const clang::SwitchStmt* switchStmt, location l, location commentLocation,
  list& /*<statement>*/stmts, ForwardReferenceList& forwardStmts,
  const clang::DeclContext* context, bool* shouldDelay) {
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  exp_node condition = makeExpression(switchStmt->getCond(), shouldDelay);
  expression econdition = expression_cons(makeLocation(
      switchStmt->getCond()->getSourceRange()), condition);

  if (!_cleanups.empty()) {
    const char* conditionVarname = mk_tmp_name ();
    forwardStmts.insertContainer(statement_VarDecl(copy_loc(l),
        conditionVarname, qual_type_cons(NULL, typ_Int(IBOOL)), opt_none()));
    /* statement */ list* cursor = forwardStmts.getBack()
      ? &forwardStmts.getBack()->next : &forwardStmts.getFront();
    forwardStmts.insertContainer(statement_Expression(
      copy_loc(econdition->eloc), expression_cons(copy_loc(econdition->eloc),
        exp_node_Assign(
          expression_cons(copy_loc(econdition->eloc), exp_node_Variable(
              variable_Local(qualified_name_cons(NULL,
                strdup(conditionVarname))))),
          econdition))));
    makeCleanupsStmts(forwardStmts, cursor, econdition, context, shouldDelay);
    econdition = expression_cons(copy_loc(econdition->eloc),
      exp_node_Variable(variable_Local(qualified_name_cons(NULL,
        strdup(conditionVarname)))));
  };

  const clang::VarDecl* declared_var = switchStmt->getConditionVariable();
  cond_statement cond = makeCondition(declared_var, econdition, shouldDelay);
  std::vector<const clang::SwitchCase*> reverseCaseList;
  const clang::SwitchCase* currentCase = switchStmt->getSwitchCaseList();
  while (currentCase) {
    reverseCaseList.push_back(currentCase);
    currentCase = currentCase->getNextSwitchCase();
  };
  std::vector<const clang::SwitchCase*>::const_reverse_iterator
      caseIterator = reverseCaseList.rbegin();
  std::vector<const clang::SwitchCase*>::const_reverse_iterator
      endCaseIterator = reverseCaseList.rend();

  assert(switchStmt->getBody()->getStmtClass() == clang::Stmt::CompoundStmtClass
      && llvm::dyn_cast<clang::CompoundStmt>(switchStmt->getBody()));
  const clang::CompoundStmt *block
      = static_cast<const clang::CompoundStmt*>(switchStmt->getBody());
  addLabelsInSema(block);
  clang::CompoundStmt::const_body_iterator it = block->body_begin();
  clang::CompoundStmt::const_body_iterator last = block->body_end();
  list /*<case_statement>*/ cases = NULL;
  ForwardReferenceList forwardCases(cases);
  while (caseIterator != endCaseIterator) {
    currentCase = *caseIterator;
    unsigned caseClass = currentCase->getStmtClass();
    assert((caseClass == clang::Stmt::CaseStmtClass)
        || (caseClass == clang::Stmt::DefaultStmtClass));
    case_statement newCase;
    list /*<statement>*/ instructions = NULL;
    ForwardReferenceList forwardInstructions;
    while (*it != currentCase && it != last)
      ++it;
    ++caseIterator;
    const clang::SwitchCase* nextCase = (caseIterator != endCaseIterator)
        ? *caseIterator : NULL;
    if (caseClass == clang::Stmt::CaseStmtClass) {
      compilation_constant cst = makeConstantExpression(
          static_cast<const clang::CaseStmt*>(currentCase)->getLHS());
      newCase = case_statement_Case(cst,instructions);
      forwardInstructions = ForwardReferenceList(newCase->cons_case_statement
          .Case.instructions);
    }
    else { // (caseClass == clang::Stmt::DefaultStmtClass)
      newCase = case_statement_Default(instructions);
      forwardInstructions = ForwardReferenceList(newCase->cons_case_statement
          .Default.instructions);
    };
    forwardCases.insertContainer(newCase);
    if (it != last && *it == currentCase) {
      if (currentCase->getSubStmt()->getStmtClass()
            == clang::Stmt::CaseStmtClass) {
        do {
          assert(currentCase->getSubStmt() == nextCase);
          compilation_constant cst = makeConstantExpression(
              static_cast<const clang::CaseStmt*>(nextCase)->getLHS());
          newCase = case_statement_Case(cst,instructions);
          forwardInstructions = ForwardReferenceList(newCase
              ->cons_case_statement.Case.instructions);
          forwardCases.insertContainer(newCase);
          currentCase = nextCase;
          ++caseIterator;
          nextCase = (caseIterator != endCaseIterator)
              ? *caseIterator : NULL;
        } while (currentCase->getSubStmt()->getStmtClass()
          == clang::Stmt::CaseStmtClass);
      };
      if (currentCase->getSubStmt()->getStmtClass()
            == clang::Stmt::DefaultStmtClass) {
        assert(currentCase->getSubStmt() == nextCase);
        newCase = case_statement_Default(instructions);
        forwardInstructions = ForwardReferenceList(newCase->cons_case_statement
            .Default.instructions);
        forwardCases.insertContainer(newCase);
        currentCase = nextCase;
        ++caseIterator;
        nextCase = (caseIterator != endCaseIterator)
            ? *caseIterator : NULL;
      };
      statement instruction = makeStmt(currentCase->getSubStmt(),
          &forwardInstructions, context, shouldDelay);
      forwardInstructions.insertContainer(instruction);
      ++it;
    };
    if (currentCase != nextCase) {
      while (it != last && *it != nextCase) {
        statement instruction = makeStmt(*it, &forwardInstructions,
            context, shouldDelay);
        forwardInstructions.insertContainer(instruction);
        ++it;
      };
    };
    currentCase = nextCase;
  };
  statement result = statement_Switch(l,cond,cases);
  if (stmts) {
    forwardStmts.insertContainer(result);
    if (commentLocation) {
      commentLocation = copy_loc(commentLocation);
      extend_location_with(commentLocation, l);
    }
    else
      commentLocation = copy_loc(l);
  };
  return !stmts ? result
                : statement_Block(commentLocation, stmts);
}

statement FramacVisitor::makeWhileStmt(
  const clang::WhileStmt* whileStmt, location l, location commentLocation,
  list& /*<statement>*/stmts, ForwardReferenceList& forwardStmts,
  const clang::DeclContext* context, bool* shouldDelay) {
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  exp_node condition = makeExpression(whileStmt->getCond(), shouldDelay);
  expression econdition = expression_cons(makeLocation(
      whileStmt->getCond()->getSourceRange()), condition);

  const char* conditionVarname = NULL;
  if (!_cleanups.empty()) {
    conditionVarname = mk_tmp_name ();
    forwardStmts.insertContainer(statement_VarDecl(copy_loc(l),
        conditionVarname, qual_type_cons(NULL, typ_Int(IBOOL)), opt_none()));
    /* statement */ list* cursor = forwardStmts.getBack()
      ? &forwardStmts.getBack()->next : &forwardStmts.getFront();
    forwardStmts.insertContainer(statement_Expression(
      copy_loc(econdition->eloc), expression_cons(copy_loc(econdition->eloc),
        exp_node_Assign(
          expression_cons(copy_loc(econdition->eloc), exp_node_Variable(
              variable_Local(qualified_name_cons(NULL,
                strdup(conditionVarname))))),
          expression_dup(econdition)))));
    std::vector<const clang::ExprWithCleanups*> cleanups = _cleanups;
    makeCleanupsStmts(forwardStmts, cursor, econdition, context, shouldDelay);
    cleanups.swap(_cleanups);
  };
  statement body = makeStmt(whileStmt->getBody(),NULL, context, shouldDelay);
  if (!_cleanups.empty()) {
    if (body->tag_statement != BLOCK)
      body = statement_Block(copy_loc(l), cons_container(body, NULL));
    ForwardReferenceList block(body->cons_statement.Block.instructions);
    /* statement */ list* cursor = forwardStmts.getBack()
      ? &forwardStmts.getBack()->next : &forwardStmts.getFront();
    // /* statement */ list* cursor = block.getBack()
    //   ? &block.getBack()->next : &block.getFront();
    block.insertContainer(statement_Expression(
      copy_loc(econdition->eloc), expression_cons(copy_loc(econdition->eloc),
        exp_node_Assign(
          expression_cons(copy_loc(econdition->eloc), exp_node_Variable(
              variable_Local(qualified_name_cons(NULL,
                strdup(conditionVarname))))),
          econdition))));
    makeCleanupsStmts(block, cursor, econdition, context, shouldDelay);
    econdition = expression_cons(copy_loc(econdition->eloc),
      exp_node_Variable(variable_Local(qualified_name_cons(NULL,
        strdup(conditionVarname)))));
  };
  statement result = statement_While(l,econdition,body,NULL);
  _delayedLoopAnnotation.attachTo(whileStmt, result);
  if (stmts) {
    forwardStmts.insertContainer(result);
    if (commentLocation) {
      commentLocation = copy_loc(commentLocation);
      extend_location_with(commentLocation, l);
    }
    else
      commentLocation = copy_loc(l);
  };
  return !stmts ? result
                : statement_Block(commentLocation, stmts);
}

catch_block FramacVisitor::makeCatchBlock(
  const clang::CXXCatchStmt* catchStmt, location l, location commentLocation,
  const clang::DeclContext* context, bool* shouldDelay) {
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  /* statement */ list stmts = NULL;
  ForwardReferenceList forwardStmts(stmts);
  /* arg_decl */ option declVariable;
  if (catchStmt->getExceptionDecl()) {
    char* name;
    if (catchStmt->getExceptionDecl()->getName().size() > 0)
      name = strdup(catchStmt->getExceptionDecl()->getName().str().c_str());
    else
      name = strdup("__frama_c_arg_except");
    declVariable =
      opt_some_container(
        arg_decl_cons(
          makeDefaultType(
            _clangUtils->getBeginLoc(*catchStmt), catchStmt->getCaughtType()),
          name, makeLocation(catchStmt->getSourceRange())));
  }
  else
    declVariable = opt_none();
  statement block = makeStmt(catchStmt->getHandlerBlock(),
      &forwardStmts, context, shouldDelay);
  if (block->tag_statement == BLOCK) {
    /* statement */ list blockList = block->cons_statement.Block.instructions;
    block->cons_statement.Block.instructions = NULL;
    free_statement(block);
    ForwardReferenceList blockStmts(blockList);
    forwardStmts.append(blockStmts);
  }
  else
    forwardStmts.insertContainer(block);
  return catch_block_cons(declVariable, stmts);
}

statement FramacVisitor::makeTryStmt(
  const clang::CXXTryStmt* tryStmt, location l, location commentLocation,
  list& /*<statement>*/stmts, ForwardReferenceList* container,
  ForwardReferenceList& forwardStmts, const clang::DeclContext* context,
  bool* shouldDelay) {
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  _parents.pushBlock();
  clang::CompoundStmt::const_body_iterator
      it = tryStmt->getTryBlock()->body_begin(),
      last = tryStmt->getTryBlock()->body_end();
  while(it < last) {
    statement stmt = makeStmt(*it, &forwardStmts, context, shouldDelay);
    forwardStmts.insertContainer(stmt);
    it++;
  }
  _parents.popBlock();
  readStatementCommentUntil(
    _clangUtils->getEndLoc(*tryStmt),
    commentLocation, stmts, forwardStmts, NULL, container, context);
  statement result = statement_TryCatch(l, stmts, NULL);
  /* catch_block */ list* endCatch = &result->cons_statement.TryCatch.cases;
  int numOfCatch = tryStmt->getNumHandlers();
  for (int blockNumber = 0; blockNumber < numOfCatch; ++blockNumber) {
    *endCatch = cons_container(makeCatchBlock(tryStmt->getHandler(blockNumber),
      makeLocation(tryStmt->getHandler(blockNumber)->getSourceRange()),
      commentLocation, context, shouldDelay), NULL);
    endCatch = &(*endCatch)->next;
  };
  return result;
}
statement FramacVisitor::makeGCCAsmStmt(const clang::GCCAsmStmt* asmStmt) {
    location loc = makeLocation(asmStmt->getSourceRange());

    list qualifier = NULL;
    list instructions = NULL;

    if(asmStmt->isVolatile())
        qualifier = cons_plain(VOLATILE, qualifier);

    instructions = cons_container(
                     (void*)strdup(
                       asmStmt->getAsmString()->getString().str().c_str()),
                     instructions);
    asm_IO asm_IO_cons(option aIO_name, const char * constraints, expression
    expr);
    list inputs = NULL;
    list outputs = NULL;
    list clobbers = NULL;
    list labels = NULL;
    bool f = false; // only used because of the API of makeExpression.

    for(int i=asmStmt->getNumInputs()-1; i >= 0; --i)
    {
        std::string name = asmStmt->getInputName(i).str();
        inputs = cons_container(
             asm_IO_cons(
               name.empty() ?
                 opt_none() :
                 opt_some_container((void*) const_cast<char*>(name.c_str())),
               strdup(asmStmt->getInputConstraint(i).str().c_str()),
               expression_cons(
                 copy_loc(loc),
                 makeExpression(asmStmt->getInputExpr(i), &f))),
             inputs);
    }

    for(int i=asmStmt->getNumOutputs()-1; i >= 0; --i)
    {
        std::string name = asmStmt->getOutputName(i).str();
        outputs = cons_container(
              asm_IO_cons(
                name.empty() ?
                  opt_none() :
                  opt_some_container((void*)const_cast<char*>(name.c_str())),
                strdup(asmStmt->getOutputConstraint(i).str().c_str()),
                expression_cons(
                  copy_loc(loc),
                  makeExpression(
                    asmStmt->getOutputExpr(i), &f))),
              outputs);
    }

    assert(!f); // we should not have to delay this definition

    for(unsigned int i=0; i<asmStmt->getNumClobbers(); ++i)
        clobbers = cons_container(
                     (void*)strdup(asmStmt->getClobber(i).str().c_str()),
                     clobbers);

    asm_details details = asm_details_cons(outputs, inputs, clobbers, labels);
    return statement_GccInlineAsm(
             loc,
             qualifier,
             instructions,
             opt_some_container((void*)details));
}

statement FramacVisitor::makeStmt(
  const clang::Stmt* stmt, ForwardReferenceList* container,
  const clang::DeclContext* context, bool* shouldDelay) {
  clang::SourceRange clangRange = stmt->getSourceRange();
  list /*<statement>*/stmts = NULL;
  ForwardReferenceList forwardStmts(stmts);
  
  location commentLocation = NULL;
  // implicit precondition: commentLocation != NULL => stmts != NULL
  // the reverse is not true any more thanks to cleanups
  readStatementCommentUntil(clangRange.getBegin(), commentLocation,
    stmts, forwardStmts, stmt, container, context);

  location l = makeLocation(clangRange);
  clang::Stmt::StmtClass stmtClass = stmt->getStmtClass();
  
  if (stmtClass >= clang::Stmt::firstExprConstant
      && stmtClass <= clang::Stmt::lastExprConstant
      && stmtClass != clang::Stmt::ExprWithCleanupsClass) {
    assert(llvm::dyn_cast<clang::Expr>(stmt));
    exp_node stmtExpr = makeExpression(static_cast<const clang::Expr*>(stmt),
                                       shouldDelay);
    expression estmtExpr = expression_cons(makeLocation(stmt->getSourceRange()),
        stmtExpr);
    if (stmts || !_cleanups.empty()) {
      /* statement */ list* cursor = forwardStmts.getBack()
        ? &forwardStmts.getBack()->next : &forwardStmts.getFront();
      forwardStmts.insertContainer(statement_Expression(l, estmtExpr));
      if (!_cleanups.empty())
        makeCleanupsStmts(forwardStmts,cursor, estmtExpr, context, shouldDelay);
      if (commentLocation) {
        commentLocation = copy_loc(commentLocation);
        extend_location_with(commentLocation, l);
      }
      else
        commentLocation = copy_loc(l);
    };
    return !stmts ? statement_Expression(l, estmtExpr)
                  : statement_Block(commentLocation, stmts);
  };

  switch (stmtClass) {
    case clang::Stmt::ExprWithCleanupsClass:
      assert(llvm::dyn_cast<clang::ExprWithCleanups>(stmt));
      return makeExprWithCleanupsStmt(
          static_cast<const clang::ExprWithCleanups*>(stmt), l, stmts,
          forwardStmts, context, shouldDelay);
    case clang::Stmt::BreakStmtClass:
      if (stmts) {
        forwardStmts.insertContainer(statement_Break(l));
        if (commentLocation) {
          commentLocation = copy_loc(commentLocation);
          extend_location_with(commentLocation, l);
        }
        else
          commentLocation = copy_loc(l);
      };
      return !stmts ? statement_Break(l)
                    : statement_Block(commentLocation, stmts);
    case clang::Stmt::ContinueStmtClass:
      if (stmts) {
        forwardStmts.insertContainer(statement_Continue(l));
        if (commentLocation) {
          commentLocation = copy_loc(commentLocation);
          extend_location_with(commentLocation, l);
        }
        else
          commentLocation = copy_loc(l);
      };
      return !stmts ? statement_Continue(l)
                    : statement_Block(commentLocation, stmts);
    case clang::Stmt::CompoundStmtClass:
      assert(llvm::dyn_cast<clang::CompoundStmt>(stmt));
      return makeCompoundStmt(
          static_cast<const clang::CompoundStmt*>(stmt), l, commentLocation,
          stmts, container, forwardStmts, context, shouldDelay);
    case clang::Stmt::ReturnStmtClass:
      assert(llvm::dyn_cast<clang::ReturnStmt>(stmt));
      return makeReturnStmt(static_cast<const clang::ReturnStmt*>(stmt),
          l, commentLocation, stmts, forwardStmts, context, shouldDelay);
    case clang::Stmt::DeclStmtClass:
      assert(llvm::dyn_cast<clang::DeclStmt>(stmt));
      return makeDeclStmt(static_cast<const clang::DeclStmt*>(stmt),
          l, commentLocation, stmts, container, forwardStmts, context,
          shouldDelay);
    case clang::Stmt::CXXDefaultArgExprClass:
      { assert(llvm::dyn_cast<clang::Expr>(stmt));
        exp_node stmtExpr = makeExpression(
          static_cast<const clang::Expr*>(stmt), shouldDelay);
        expression estmtExpr
            = expression_cons(makeLocation(stmt->getSourceRange()), stmtExpr);
        if (stmts || !_cleanups.empty()) {
          /* statement */ list* cursor = forwardStmts.getBack()
            ? &forwardStmts.getBack()->next : &forwardStmts.getFront();
          forwardStmts.insertContainer(statement_Expression(l, estmtExpr));
          if (!_cleanups.empty())
            makeCleanupsStmts(forwardStmts, cursor, estmtExpr, context,
              shouldDelay);
          if (commentLocation) {
            commentLocation = copy_loc(commentLocation);
            extend_location_with(commentLocation, l);
          }
          else
            commentLocation = copy_loc(l);
        };
        return !stmts ? statement_Expression(l, estmtExpr)
                      : statement_Block(commentLocation, stmts);
      };
    case clang::Stmt::DoStmtClass:
      assert(llvm::dyn_cast<clang::DoStmt>(stmt));
      return makeDoStmt(static_cast<const clang::DoStmt*>(stmt),
          l, commentLocation, stmts, forwardStmts, context, shouldDelay);
    case clang::Stmt::ForStmtClass:
      assert(llvm::dyn_cast<clang::ForStmt>(stmt));
      return makeForStmt(static_cast<const clang::ForStmt*>(stmt),
          l, commentLocation, stmts, forwardStmts, context, shouldDelay);
    case clang::Stmt::LabelStmtClass:
      assert(llvm::dyn_cast<clang::LabelStmt>(stmt));
      return makeLabelStmt(static_cast<const clang::LabelStmt*>(stmt), l,
        commentLocation, stmts, container, forwardStmts, context, shouldDelay);
    case clang::Stmt::GotoStmtClass:
      { assert(llvm::dyn_cast<clang::GotoStmt>(stmt));
        const clang::GotoStmt* gotoStmt
            = static_cast<const clang::GotoStmt*>(stmt);
        statement result = statement_Goto(l, strdup(
            gotoStmt->getLabel()->getName().str().c_str()));
        if (stmts) {
          forwardStmts.insertContainer(result);
          if (commentLocation) {
            commentLocation = copy_loc(commentLocation);
            extend_location_with(commentLocation, l);
          }
          else
            commentLocation = copy_loc(l);
        };
        return !stmts ? result
                      : statement_Block(commentLocation, stmts);
      }
    case clang::Stmt::IfStmtClass:
      assert(llvm::dyn_cast<clang::IfStmt>(stmt));
      return makeIfStmt(static_cast<const clang::IfStmt*>(stmt),
          l, commentLocation, stmts, forwardStmts, context, shouldDelay);
    case clang::Stmt::SwitchStmtClass:
      assert(llvm::dyn_cast<clang::SwitchStmt>(stmt));
      return makeSwitchStmt(static_cast<const clang::SwitchStmt*>(stmt),
          l, commentLocation, stmts, forwardStmts, context, shouldDelay);
    case clang::Stmt::WhileStmtClass:
      assert(llvm::dyn_cast<clang::WhileStmt>(stmt));
      return makeWhileStmt(static_cast<const clang::WhileStmt*>(stmt),
          l, commentLocation, stmts, forwardStmts, context, shouldDelay);
    
    case clang::Stmt::ParenExprClass: {
      const clang::ParenExpr* pe = llvm::dyn_cast<const clang::ParenExpr>(stmt);
      assert(pe);
      return makeStmt(pe->getSubExpr(), container, context , shouldDelay);
    }

    case clang::Stmt::NullStmtClass:
      if (stmts) {
        if (commentLocation) {
          commentLocation = copy_loc(commentLocation);
          extend_location_with(commentLocation, l);
          free_location(l);
        }
        else
          commentLocation = l;
      };
      return !stmts ? statement_Nop(l)
                    : statement_Block(commentLocation, stmts);
    case clang::Stmt::CXXTryStmtClass:
      assert(llvm::dyn_cast<clang::CXXTryStmt>(stmt));
      return makeTryStmt(static_cast<const clang::CXXTryStmt*>(stmt), l,
        commentLocation, stmts, container, forwardStmts, context, shouldDelay);
    case clang::Stmt::CXXCatchStmtClass:
      assert(false);
      std::cerr << "Unsupported direct catch:\n";
#if CLANG_VERSION_MAJOR >= 11
      stmt->dump(llvm::errs(), *_context);
#else
      stmt->dump(llvm::errs(), _context->getSourceManager());
#endif
      std::cerr << "\nAborting\n";
      exit(2);

    case clang::Stmt::GCCAsmStmtClass:
      assert(llvm::dyn_cast<clang::GCCAsmStmt>(stmt));
      return makeGCCAsmStmt(static_cast<const clang::GCCAsmStmt*>(stmt));

    case clang::Stmt::MSAsmStmtClass:
    case clang::Stmt::AttributedStmtClass:
    case clang::Stmt::CXXForRangeStmtClass:
    case clang::Stmt::IndirectGotoStmtClass:
    case clang::Stmt::MSDependentExistsStmtClass:
    default:
      std::cerr << "Unsupported Statement:\n";
#if CLANG_VERSION_MAJOR >= 11
      stmt->dump(llvm::errs(), *_context);
#else
      stmt->dump(llvm::errs(), _context->getSourceManager());
#endif
      std::cerr << "\nAborting\n";
      exit(2);
  }
}

list /*<statement>*/ FramacVisitor::makeCodeBlock(
  const clang::Stmt* body, const clang::DeclContext* context,
  const clang::FunctionDecl* Decl, bool* shouldDelay) {
  list /*<statement>*/stmts = NULL;
  ForwardReferenceList forwardStmts(stmts);
  switch (body->getStmtClass()) {
    case clang::Stmt::CompoundStmtClass:
      { const clang::CompoundStmt *block
            = static_cast<const clang::CompoundStmt *>(body);
        addLabelsInSema(block);
        clang::CompoundStmt::const_body_iterator it = block->body_begin();
        clang::CompoundStmt::const_body_iterator last = block -> body_end();
        while(it < last) {
          statement stmt = makeStmt(*it, &forwardStmts, context, shouldDelay);
          forwardStmts.insertContainer(stmt);
          it++;
        }
        break;
      }
    default:
      { statement single = makeStmt(body, &forwardStmts, context, shouldDelay);
        forwardStmts.insertContainer(single);
        break;
      }
  }

  location commentLocation = NULL;
  readStatementCommentUntil(Decl->getSourceRange().getEnd(),
    commentLocation, stmts, forwardStmts, NULL, NULL, context);
  if (commentLocation)
    free_location(commentLocation);
  return stmts;
}

void FramacVisitor::ensureVaListDeclaration(const clang::RecordDecl* decl) {
  if (!_generatedVaList) {
    _generatedVaList = true;
    tkind templateParameters = NULL;
    const char* name = _clangUtils->get_aggregate_name(decl, &templateParameters);
    location loc = makeLocation(decl->getSourceRange());
    ckind type;
    switch (decl->getTagKind()) {
      case clang::TTK_Struct:
        type = CSTRUCT;
        break;
      case clang::TTK_Class:
        type = CCLASS;
        break;
      case clang::TTK_Union:
        type = CUNION;
        break;
      default:
        std::cerr << "Unsupported Record Declaration:"
            << name
            << "\nAborting\n";
        exit(2);
    };
    /* inheritance list */ option inherits = opt_none();
    decl_or_impl_name decl_name = decl_or_impl_name_Declaration(name);
    translation_unit_decl compound = translation_unit_decl_Compound(loc,
        decl_name, type, inherits, opt_some_container(NULL), false,
        templateParameters, Clang_utils::isExternCContext(decl),_ghost);
    _globals.insertBeforeContainer(compound);
    ForwardReferenceList content(*(/* class_decl */ list*)
        &((option) compound->cons_translation_unit_decl.Compound.body)
           ->content.container);
    clang::RecordDecl::decl_iterator
      declIterEnd = decl->decls_end(), declIter = decl->decls_begin();
    for (; declIter != declIterEnd; ++declIter) {
      clang::Decl* Child = *declIter;
      clang::Decl::Kind kind = Child->getKind();
      if (kind == clang::Decl::Field) {
        const clang::FieldDecl* fieldDecl = (const clang::FieldDecl*) Child;
        const char* name = _clangUtils->get_field_name(fieldDecl);
        location loc = makeLocation(fieldDecl->getSourceRange());
        clang::QualType type = fieldDecl->getType();
        qual_type declarationType;
        declarationType = makeDefaultType(fieldDecl->getLocation(), type);
        class_decl decl =
          class_decl_CFieldDecl(
            loc, name, declarationType, opt_none(), fieldDecl->isMutable());
        content.insertContainer(decl);
      }
      else if (kind >= clang::Decl::firstFunction
          && kind <= clang::Decl::lastFunction) {
        auto functionDecl = (const clang::FunctionDecl*) Child;
        if (!functionDecl->isDeleted()) {
          const char* name = copy_string(functionDecl->getNameAsString ());
          tkind templateExtension = tkind_TStandard();
          location loc = makeLocation(functionDecl->getSourceRange());
          qual_type return_type =
	    makeDefaultExternalNameType(
	    functionDecl->getLocation(),
            functionDecl->getReturnType());
          list /*arg_decl*/ prms = NULL;
          for (int i = functionDecl->getNumParams() - 1;i>=0;i--) {
            arg_decl cdecl = (arg_decl)malloc(sizeof(*cdecl));
            prms = cons_container(cdecl,prms);
            const clang::ParmVarDecl *prm = functionDecl->getParamDecl(i);
            if (prm->getNameAsString().size() > 0)
              cdecl->arg_name = copy_string(prm->getNameAsString());
            else {
              std::ostringstream out;
              out << "__frama_c_arg_" << i;
              cdecl->arg_name = copy_string(out.str());
            };
            cdecl->arg_type =
              makeDefaultExternalNameType(
                prm->getLocation(), prm->getOriginalType());
            cdecl->arg_loc = makeLocation(prm->getSourceRange());
          }
          // Body
          option /*statement*/ body = opt_none();
          bool isImplicitFunction = false;
          if (functionDecl->isThisDeclarationADefinition()) {
            if (clang::CXXMethodDecl::classof(functionDecl)) {
              const clang::CXXMethodDecl* meth
                = static_cast<const clang::CXXMethodDecl*>(functionDecl);
              if (_clangUtils->doesGenerateImplicitMethods()
                  || meth->isUserProvided()) {
                list /*<statement>*/ funContent = makeCodeBlock(
                  functionDecl->getBody(),
                  functionDecl->getDeclContext(), functionDecl);
                free(body);
                body = opt_some_container(funContent);
              }
              else
                isImplicitFunction = true;
            }
            else {
              list /*<statement>*/ funContent = makeCodeBlock(
                functionDecl->getBody(),
                functionDecl->getDeclContext(), functionDecl);
              free(body);
              body = opt_some_container(funContent);
            };
          };

          const clang::RecordDecl* current_class = decl;
          funkind kind;
          if (functionDecl->getCanonicalDecl()->getStorageClass()
              == clang::SC_Static)
            kind = funkind_FKFunction();
          else {
            const auto meth =
              llvm::cast<const clang::CXXMethodDecl>(functionDecl);
            if (clang::CXXMethodDecl::classof(functionDecl)) {
            };
            arg_decl cdecl = (arg_decl)malloc(sizeof(*cdecl));
            prms = cons_container(cdecl,prms);
            cdecl->arg_name = copy_string("this");
            clang::QualType typeClass(current_class->getTypeForDecl(), 0);
            qual_type this_type =
              _clangUtils->makeType(functionDecl->getLocation(), typeClass);
            this_type->qualifier = cv_this_ptr(meth);
            pkind pointerKind = pkind_PDataPointer(this_type);
            cdecl->arg_type = qual_type_cons(NULL, typ_Pointer(pointerKind));
            cdecl->arg_loc = makeLocation(functionDecl->getSourceRange());
            if (llvm::isa<clang::CXXConstructorDecl>(meth)) {
              kind = funkind_FKConstructor(true);
              if (strlen(name) == 0) {
                free(const_cast<char*>(name));
                name = strdup("constructor-special");
              }
              if (body->is_some) {
                list& contentBody = *(list*)&body->content.container;
                insertConstructorPreambleIn(
                  *llvm::cast<const clang::CXXConstructorDecl>(meth),
                  contentBody,NULL);
              }
            } else if (llvm::isa<clang::CXXDestructorDecl>(meth)) {
              kind = funkind_FKDestructor(true);
              if (body->is_some)
                insertDestructorPreambleIn(
                *llvm::cast<const clang::CXXDestructorDecl>(meth), body);
            } else {
              kind = _clangUtils->cv_meth(meth);
            }
          }
          bool is_implicit = isImplicitFunction || functionDecl->isDefaulted();
          bool is_variadic = functionDecl->isVariadic();
          class_decl method = class_decl_CMethod(
            loc, name, kind, return_type,
            prms, is_variadic, body, is_implicit, templateExtension,
            false /* has_further_definition */,
            opt_none() /* throws */,
            opt_none() /* contract */);
          content.insertContainer(method);
        };
      }
    }
    _tableForWaitingDeclarations.addDeclaration(decl, _globals);
  }
}

void FramacVisitor::insertNamedDeclaration(
  const clang::Decl* decl, bool isBefore) {
  clang::Decl::Kind kindDecl = decl->getKind();
  if (kindDecl >= clang::Decl::firstNamed && kindDecl <= clang::Decl::lastNamed)
  { assert(llvm::dyn_cast<clang::NamedDecl>(decl));
    qualified_name name = _clangUtils->makeQualifiedName(
        *static_cast<const clang::NamedDecl*>(decl));
    decl_or_impl_name decl_name = decl_or_impl_name_Implementation(name);
    translation_unit_decl resultDecl = NULL;
    if (kindDecl >= clang::Decl::firstRecord
        && kindDecl <= clang::Decl::lastRecord) {
      assert(llvm::dyn_cast<clang::RecordDecl>(decl));
      const clang::RecordDecl* recordDecl
          = static_cast<const clang::RecordDecl*>(decl);
      location loc = makeLocation(recordDecl->getSourceRange());
      switch (recordDecl->getTagKind()) {
        case clang::TTK_Struct:
          free(const_cast<char*>(name->decl_name));
          { tkind templateParameters = NULL;
            name->decl_name = _clangUtils->get_aggregate_name(recordDecl,
                &templateParameters);
            if (!_generatedVaList && !recordDecl->isFromASTFile()
                && strcmp(name->decl_name, "__va_list_tag") == 0)
              ensureVaListDeclaration(recordDecl);
            resultDecl =
              translation_unit_decl_Compound(
                loc, decl_name, CSTRUCT, opt_none(), opt_none(),
                false, templateParameters,
                Clang_utils::isExternCContext(recordDecl),
                false /* ghost */);
          };
          break;
        case clang::TTK_Class:
          free(const_cast<char*>(name->decl_name));
          { tkind templateParameters = NULL;
            name->decl_name = _clangUtils->get_aggregate_name(recordDecl,
                &templateParameters);
            resultDecl =
              translation_unit_decl_Compound(
                loc, decl_name, CCLASS, opt_none(), opt_none(),
                false, templateParameters,
                Clang_utils::isExternCContext(recordDecl), _ghost);
          };
          break;
        case clang::TTK_Union:
          free(const_cast<char*>(name->decl_name));
          { tkind templateParameters = NULL;
            name->decl_name = _clangUtils->get_aggregate_name(recordDecl,
                &templateParameters);
            resultDecl =
              translation_unit_decl_Compound(
                loc, decl_name, CUNION, opt_none(), opt_none(), false,
                templateParameters, Clang_utils::isExternCContext(recordDecl),
                _ghost);
          };
          break;
        case clang::TTK_Enum:
          resultDecl =
            translation_unit_decl_EnumDecl(
              loc, decl_name, IINT,
              /* compilation_constant list option */ opt_none(),
              Clang_utils::isExternCContext(recordDecl),
              _ghost);
          break;
        default:
          free_location(loc);
          break;
      };
    }
    else if (kindDecl >= clang::Decl::firstType
        && kindDecl <= clang::Decl::lastType) {
      location loc = makeLocation(decl->getSourceRange());
      if (kindDecl == clang::Decl::Enum)
        resultDecl =
          translation_unit_decl_EnumDecl(
            loc, decl_name, IINT,
            /* compilation_constant list option */ opt_none(),
            Clang_utils::isExternCContext(decl->getDeclContext()), _ghost);
      else if (kindDecl == clang::Decl::Typedef) {
        const clang::TypedefDecl* typedefDecl=(const clang::TypedefDecl*) decl;
        if (_generatedTypedefsInAdvance.find(typedefDecl)
            == _generatedTypedefsInAdvance.end()) {
          qual_type targetType =
            makeDefaultExternalNameType(
              typedefDecl->getLocation(), typedefDecl->getUnderlyingType());
          resultDecl =
            translation_unit_decl_Typedef(
              loc, decl_name, targetType, false, _ghost);
          _generatedTypedefsInAdvance.insert(typedefDecl);
        };
        // mainly for __va_list_tag;
        // resultDecl = translation_unit_decl_Typename(loc, name);
      }
      else {
         decl_or_impl_name decl_name = decl_or_impl_name_Implementation(name);
         resultDecl =
           translation_unit_decl_Compound(
             loc, decl_name, CCLASS,
             opt_none(), opt_none(), false, tkind_TStandard(), false, _ghost);
         assert(false);
      };
    }
    if (resultDecl) {
      if (isBefore)
         _globals.insertBeforeContainer(resultDecl);
      else
         _globals.insertContainer(resultDecl);
    }
    else
      free_decl_or_impl_name(decl_name);
  };
}

qual_type FramacVisitor::makeDefaultType(
  clang::SourceLocation const& loc, clang::QualType const& type)
{ qual_type result;
  if (hasInstanceContext()) {
    FramaCIRGenAction::UnvisitedRegistration unvisitedRegistration(*this);
    result = _clangUtils->makeType(loc, type, &unvisitedRegistration);
  }
  else {
    FramaCIRGenAction::VerifyNameRegistration insertMissingDeclRegistration(
        const_cast<FramacVisitor&>(*this));
    result = _clangUtils->makeType(loc, type, &insertMissingDeclRegistration);
  };
  return result;
}

qual_type FramacVisitor::makeDefaultNameType(
  clang::SourceLocation const& loc, clang::QualType const& type)
{ qual_type result;
  if (hasInstanceContext()) {
    FramaCIRGenAction::UnvisitedNameRegistration unvisitedRegistration(*this);
    result = _clangUtils->makeType(loc, type, &unvisitedRegistration);
  }
  else {
    FramaCIRGenAction::VerifyNameRegistration insertMissingDeclRegistration(
        const_cast<FramacVisitor&>(*this));
    result = _clangUtils->makeType(loc, type, &insertMissingDeclRegistration);
  };
  return result;
}

qual_type FramacVisitor::makeDefaultExternalNameType(
    clang::SourceLocation const& loc, clang::QualType const& type)
{ qual_type result;
  if (hasInstanceContext()) {
    FramaCIRGenAction::UnvisitedNameRegistration unvisitedRegistration(
        const_cast<FramacVisitor&>(*this));
    unvisitedRegistration.setExternal();
    result = _clangUtils->makeType(loc, type, &unvisitedRegistration);
  }
  else {
    FramaCIRGenAction::VerifyNameRegistration
      insertMissingDeclRegistration(*this);
    result = _clangUtils->makeType(loc, type, &insertMissingDeclRegistration);
  };
  return result;
}

bool
FramaCIRGenAction::isAtTopNamespace(const clang::DeclContext* ctx) {
  while(ctx && ctx->getDeclKind()==clang::Decl::Namespace)
    ctx = ctx->getParent();
  if (ctx->getDeclKind()==clang::Decl::LinkageSpec) {
    const clang::LinkageSpecDecl* link_ctx
      = static_cast<const clang::LinkageSpecDecl*>(ctx);
    return link_ctx->getLanguage() == clang::LinkageSpecDecl::lang_c;
  }
  return !ctx->getParent();
}

int FramacVisitor::findCommentAfterRange(clang::SourceRange range) {
  int result = (int) _annotationCommentList.size()-1;
  AnnotationComment commentLocation(range);
  clang::BeforeThanCompare<AnnotationComment> Compare(
      _context->getSourceManager());
  AnnotationCommentList::iterator found = std::lower_bound(
      _annotationCommentList.begin(), _annotationCommentList.end(),
      &commentLocation, Compare);
  if (found != _annotationCommentList.end())
    result = &*found - &*_annotationCommentList.begin();
  return result;
}

void FramacVisitor::advanceCommentUntil(clang::SourceLocation sourceLocation) {
  if (_templateCommentIndex < -1) {
    while ((_commentIndex < (int) _annotationCommentList.size()-1)
        && _context->getSourceManager().isBeforeInTranslationUnit(
            _annotationCommentList[_commentIndex+1]->getSourceRange().getEnd(),
            sourceLocation))
      ++_commentIndex;
  }
  else {
    while ((_templateCommentIndex < (int) _annotationCommentList.size()-1)
      && _context->getSourceManager().isBeforeInTranslationUnit(
        _annotationCommentList[_templateCommentIndex+1]
          ->getSourceRange().getEnd(), sourceLocation))
      ++_templateCommentIndex;
  };
}

inline void FramacVisitor::parseGhostGlobal(
  AnnotationComment& comment,
  clang::DeclContext* clangContext) {
  _ghost = true;
  comment.parseGhostGlobal(
    clangContext,
    _sema,
    _parents.getScope(),
    _commentHandler.compilerInstance(),
    this);
  _ghost = false;
}

void FramacVisitor::parseGlobalComment(
  AnnotationComment& comment, const clang::DeclContext* clangContext) {
  location loc = makeLocation(comment.getSourceRange());
  loc->charnum1 += 3;
  if (comment.isLineComment())
    loc->charnum2 -= 1;
  else
    loc->charnum2 -= 3;
  ForwardReferenceList& container =
    _parents.isNamespace()?_parents.getNamespaceContent():_globals;
  comment.parseGlobal(
    container, _parents.isClass() ? &_parents.getClassContent() : NULL,
    clangContext, _context, _sema, _parents.getScope(),
    _clangUtils, _rttiTable, loc);
}

void FramacVisitor::parseGhostStatement(
  AnnotationComment& comment,
  const clang::DeclContext* clangContext,
  ForwardReferenceList* container) {
  std::cerr << "Ghost code is not supported yet" << std::endl;
  if (_clangUtils->stopOnAnnotError()) exit(2);
  else std::cerr << "Ignoring code" << std::endl;
}

void FramacVisitor::parseCodeAnnotation(
  AnnotationComment& comment,
  const clang::DeclContext* clangContext, ForwardReferenceList* container) {
  const clang::SourceLocation& clang_loc = comment.getSourceLocation();
  location loc = makeLocation(clang_loc);
  loc->charnum1 += 3;
  if (comment.isLineComment())
    loc->charnum2 -= 1;
  else
    loc->charnum2 -= 3;

  comment.parseCodeAnnotation(*container, clangContext, _context, _sema,
      _parents.getScope(), _clangUtils, _rttiTable, loc);
}

void FramacVisitor::addLocalVariablesInSema(
    const clang::DeclStmt * declStmt, clang::Scope* scope) {
  clang::DeclStmt::const_decl_iterator iter_end = declStmt->decl_end();
  for (clang::DeclStmt::const_decl_iterator
      iter = declStmt->decl_begin(); iter != iter_end; ++iter) {
    clang::Decl* singleDecl = *iter;
    clang::Decl::Kind kind = singleDecl->getKind();
    if (kind >= clang::Decl::firstNamed
        && kind <= clang::Decl::lastNamed) {
      assert(llvm::dyn_cast<clang::NamedDecl>(singleDecl));
      clang::NamedDecl* decl = static_cast<clang::NamedDecl*>(singleDecl);
      if (decl->getName().size() > 0)
        _sema->PushOnScopeChains(decl, scope, false);
      else
        scope->AddDecl(decl);
    }
    else
      scope->AddDecl(singleDecl);
  };
}

void FramacVisitor::addLocalVariablesInSema(
    const clang::CompoundStmt * block, clang::Scope* scope) {
  clang::CompoundStmt::const_body_iterator it = block->body_begin();
  clang::CompoundStmt::const_body_iterator last = block->body_end();
  while (it < last) {
    const clang::Stmt* stmt = *it;
    while (stmt->getStmtClass() == clang::Stmt::LabelStmtClass) {
      assert(llvm::dyn_cast<clang::LabelStmt>(stmt));
      const clang::LabelStmt* labelStmt
          = static_cast<const clang::LabelStmt*>(stmt);
      stmt = labelStmt->getSubStmt();
      clang::LabelDecl* decl = labelStmt->getDecl();
      if (decl->getName().size() > 0)
        _sema->PushOnScopeChains(decl, scope, false);
      else
        scope->AddDecl(decl);
    };
    
    if (stmt->getStmtClass() == clang::Stmt::DeclStmtClass) {
      assert(llvm::dyn_cast<clang::DeclStmt>(stmt));
      addLocalVariablesInSema(static_cast<const clang::DeclStmt*>(stmt), scope);
    };
    it++;
  }
}

void FramacVisitor::addLabelsInSema(const clang::CompoundStmt * block) {
  clang::CompoundStmt::const_body_iterator it = block->body_begin();
  clang::CompoundStmt::const_body_iterator last = block->body_end();
  while (it < last) {
    const clang::Stmt* stmt = *it;
    while (stmt->getStmtClass() == clang::Stmt::LabelStmtClass) {
      assert(llvm::dyn_cast<clang::LabelStmt>(stmt));
      const clang::LabelStmt* labelStmt
          = static_cast<const clang::LabelStmt*>(stmt);
      stmt = labelStmt->getSubStmt();
      _parents.addDecl(labelStmt->getDecl());
    };
    it++;
  }
}

void FramacVisitor::parseLoopAnnotation(
  AnnotationComment& comment, const clang::Stmt* stmt,
  const clang::DeclContext* clangContext, ForwardReferenceList* container) {
  const clang::SourceLocation& clang_loc = comment.getSourceLocation();
  location loc = makeLocation(clang_loc);
  loc->charnum1 += 3;
  if (comment.isLineComment())
    loc->charnum2 -= 1;
  else
    loc->charnum2 -= 3;

  const clang::CompoundStmt * foundBlock = NULL;
  clang::Scope* originalScope = _parents.getScope();
  clang::Scope* scope = originalScope;
  const clang::Stmt* foundLoop = NULL;

LAddSema:
  switch (stmt->getStmtClass()) {
    case clang::Stmt::DoStmtClass:
      { assert(llvm::dyn_cast<clang::DoStmt>(stmt));
        foundLoop = stmt;
        const clang::DoStmt* doStmt = static_cast<const clang::DoStmt*>(stmt);
        if (doStmt->getBody()->getStmtClass() == clang::Stmt::CompoundStmtClass)
        { assert(llvm::dyn_cast<clang::CompoundStmt>(doStmt->getBody()));
          foundBlock = static_cast<const clang::CompoundStmt*>(
              doStmt->getBody());
        };
      };
      break;
    case clang::Stmt::ForStmtClass:
      { assert(llvm::dyn_cast<clang::ForStmt>(stmt));
        foundLoop = stmt;
        const clang::ForStmt* forStmt= static_cast<const clang::ForStmt*>(stmt);
        if (forStmt->getInit()) {
          clang::Stmt::StmtClass initClass = forStmt->getInit()->getStmtClass();
          if (initClass == clang::Stmt::DeclStmtClass) {
            assert(llvm::dyn_cast<clang::DeclStmt>(forStmt->getInit()));
            scope = new clang::Scope(originalScope, 0, getDiagnosticEngine());
            pushCompoundScope();
            addLocalVariablesInSema(static_cast<const clang::DeclStmt*>(
                  forStmt->getInit()), scope);
          };
        };
        if (forStmt->getBody()->getStmtClass()==clang::Stmt::CompoundStmtClass)
        { assert(llvm::dyn_cast<clang::CompoundStmt>(forStmt->getBody()));
          foundBlock = static_cast<const clang::CompoundStmt*>(
              forStmt->getBody());
        };
      };
      break;
    case clang::Stmt::LabelStmtClass:
      { assert(llvm::dyn_cast<clang::LabelStmt>(stmt));
        const clang::LabelStmt* labelStmt
            = static_cast<const clang::LabelStmt*>(stmt);
        stmt = labelStmt->getSubStmt();
      };
      goto LAddSema;
    case clang::Stmt::WhileStmtClass:
      { assert(llvm::dyn_cast<clang::WhileStmt>(stmt));
        foundLoop = stmt;
        const clang::WhileStmt* whileStmt
            = static_cast<const clang::WhileStmt*>(stmt);
        if (whileStmt->getBody()->getStmtClass()
            == clang::Stmt::CompoundStmtClass)
        { assert(llvm::dyn_cast<clang::CompoundStmt>(whileStmt->getBody()));
          foundBlock = static_cast<const clang::CompoundStmt*>(
              whileStmt->getBody());
        };
      };
    default:
      break;
  }

  clang::Scope* parentLoopBody = scope;
  if (foundBlock) {
    scope = new clang::Scope(scope, 0, getDiagnosticEngine());
    pushCompoundScope();
    addLocalVariablesInSema(foundBlock, scope);
  };

  /* statement */ list result = comment.parseLoopAnnotation(clangContext,
      _context, _sema, scope, _clangUtils, _rttiTable, loc);
  if (foundLoop) {
    /* code_annotation */ list delayedResult = NULL;
    /* code_annotation */ list* backDelayedResult = &delayedResult;
    while (result) {
      statement annotStmt = (statement) result->element.container;
      assert(annotStmt->tag_statement == CODE_ANNOT);
      *backDelayedResult = cons_container(annotStmt->cons_statement.Code_annot
          .body, NULL);
      backDelayedResult = &(*backDelayedResult)->next;
      annotStmt->cons_statement.Code_annot.body = NULL;
      free_location(annotStmt->cons_statement.Code_annot.loc);
      annotStmt->cons_statement.Code_annot.loc = NULL;
      free(annotStmt);
      result->element.container = NULL;
      /* statement */ list temp = result;
      result = result->next;
      free(temp);
    };
    _delayedLoopAnnotation.setAnnotation(foundLoop, delayedResult);
  }
  else {
    ForwardReferenceList annotStmts(result);
    container->append(annotStmts);
  };

  if (foundBlock) {
    _sema->PopCompoundScope();
    delete scope;
    scope = parentLoopBody;
  };
  if (scope != originalScope) {
    _sema->PopCompoundScope();
    delete scope;
    // scope = originalScope;
  };
}

void FramacVisitor::parseStatementAnnotation(
  AnnotationComment& comment, const clang::Stmt* stmt,
  const clang::DeclContext* clangContext, ForwardReferenceList* container) {
  const clang::SourceLocation& clang_loc = comment.getSourceLocation();
  location loc = makeLocation(clang_loc);
  loc->charnum1 += 3;
  if (comment.isLineComment())
    loc->charnum2 -= 1;
  else
    loc->charnum2 -= 3;

  const clang::CompoundStmt * foundBlock = NULL;
  const clang::DeclStmt * foundDecl = NULL;
  clang::Scope* originalScope = _parents.getScope();
  clang::Scope* scope = originalScope;

LAddSema:
  switch (stmt->getStmtClass()) {
    case clang::Stmt::CompoundStmtClass:
      { assert(llvm::dyn_cast<clang::CompoundStmt>(stmt));
        foundBlock = static_cast<const clang::CompoundStmt*>(stmt);
      }
      break;
    case clang::Stmt::DeclStmtClass:
      { assert(llvm::dyn_cast<clang::DeclStmt>(stmt));
        foundDecl = static_cast<const clang::DeclStmt*>(stmt);
      };
      break;
    case clang::Stmt::DoStmtClass:
      { assert(llvm::dyn_cast<clang::DoStmt>(stmt));
        const clang::DoStmt* doStmt = static_cast<const clang::DoStmt*>(stmt);
        if (doStmt->getBody()->getStmtClass() == clang::Stmt::CompoundStmtClass)
        { assert(llvm::dyn_cast<clang::CompoundStmt>(doStmt->getBody()));
          foundBlock = static_cast<const clang::CompoundStmt*>(
              doStmt->getBody());
        };
      };
      break;
    case clang::Stmt::ForStmtClass:
      { assert(llvm::dyn_cast<clang::ForStmt>(stmt));
        const clang::ForStmt* forStmt= static_cast<const clang::ForStmt*>(stmt);
        if (forStmt->getInit()) {
          clang::Stmt::StmtClass initClass = forStmt->getInit()->getStmtClass();
          if (initClass == clang::Stmt::DeclStmtClass) {
            assert(llvm::dyn_cast<clang::DeclStmt>(forStmt->getInit()));
            scope = new clang::Scope(originalScope, 0, getDiagnosticEngine());
            pushCompoundScope();
            addLocalVariablesInSema(static_cast<const clang::DeclStmt*>(
                  forStmt->getInit()), scope);
          };
        };
        if (forStmt->getBody()->getStmtClass()==clang::Stmt::CompoundStmtClass)
        { assert(llvm::dyn_cast<clang::CompoundStmt>(forStmt->getBody()));
          foundBlock = static_cast<const clang::CompoundStmt*>(
              forStmt->getBody());
        };
      };
      break;
    case clang::Stmt::LabelStmtClass:
      { assert(llvm::dyn_cast<clang::LabelStmt>(stmt));
        const clang::LabelStmt* labelStmt
            = static_cast<const clang::LabelStmt*>(stmt);
        stmt = labelStmt->getSubStmt();
      };
      goto LAddSema;
    case clang::Stmt::WhileStmtClass:
      { assert(llvm::dyn_cast<clang::WhileStmt>(stmt));
        const clang::WhileStmt* whileStmt
            = static_cast<const clang::WhileStmt*>(stmt);
        if (whileStmt->getBody()->getStmtClass()
            == clang::Stmt::CompoundStmtClass)
        { assert(llvm::dyn_cast<clang::CompoundStmt>(whileStmt->getBody()));
          foundBlock = static_cast<const clang::CompoundStmt*>(
              whileStmt->getBody());
        };
      };
    default:
      break;
  }

  clang::Scope* parentBlockBody = scope;
  if (foundBlock) {
    scope = new clang::Scope(scope, 0, getDiagnosticEngine());
    pushCompoundScope();
    addLocalVariablesInSema(foundBlock, scope);
  }
  else if (foundDecl) {
    scope = new clang::Scope(scope, 0, getDiagnosticEngine());
    pushCompoundScope();
    addLocalVariablesInSema(foundDecl, scope);
  };

  comment.parseStatementAnnotation(*container, clangContext, _context, _sema,
      scope, _clangUtils, _rttiTable);

  if (foundBlock) {
    _sema->PopCompoundScope();
    delete scope;
    scope = parentBlockBody;
  }
  else if (foundDecl) {
    _sema->PopCompoundScope();
    delete scope;
    scope = parentBlockBody;
  };
  if (scope != originalScope) {
    _sema->PopCompoundScope();
    delete scope;
    // scope = originalScope;
  };
}

void FramacVisitor::parseFunctionContractComment(
  AnnotationComment& comment, option& /* function_contract */ contract,
  const clang::FunctionDecl* decl) {
  const clang::SourceLocation& clang_loc = comment.getSourceLocation();
  location loc = makeLocation(clang_loc);
  loc->charnum1 += 3;
  if (comment.isLineComment())
   loc->charnum2 -= 1;
  else
    loc->charnum2 -= 3;
  if (contract->is_some) {
    /* behavior */ list oldBehaviors =
      ((function_contract) contract->content.container)->behavior;
    location firstLoc = NULL;
    if (oldBehaviors) {
      behavior oldBehavior = (behavior) oldBehaviors->element.container;
      if (oldBehavior->requires)
        firstLoc = ((predicate_named) oldBehavior->requires->element.container)
          ->pred_loc;
      else if (oldBehavior->assumes)
        firstLoc = ((predicate_named) oldBehavior->assumes->element.container)
          ->pred_loc;
      else if (oldBehavior->post_cond)
        firstLoc = ((post_condition) oldBehavior->post_cond->element.container)
          ->pred->pred_loc;
      else if (oldBehavior->assignements->tag_assigns == WRITES
          && oldBehavior->assignements->cons_assigns.Writes.frm)
        firstLoc = ((from) oldBehavior->assignements->cons_assigns.Writes.frm
            ->element.container)->floc->loc;
      else if (oldBehavior->alloc->tag_allocation == FREEALLOC
          && (oldBehavior->alloc->cons_allocation.FreeAlloc.fst))
        firstLoc = ((term) oldBehavior->alloc->cons_allocation.FreeAlloc.fst
            ->element.container)->loc;
      else if (oldBehavior->alloc->tag_allocation == FREEALLOC
          && (oldBehavior->alloc->cons_allocation.FreeAlloc.snd))
        firstLoc = ((term) oldBehavior->alloc->cons_allocation.FreeAlloc.snd
            ->element.container)->loc;
      else if (oldBehavior->extended && ((behavior_extensions)
            oldBehavior->extended->element.container)->predicates)
        firstLoc = ((predicate_named) ((behavior_extensions)
          oldBehavior->extended->element.container)->predicates->element
            .container)->pred_loc;
    };
    if (firstLoc)
      std::cerr 
        << "Function " << decl->getNameInfo().getAsString()
        << " has two specifications. First one starts at file "
        << firstLoc->filename1
        << ", line "
        << firstLoc->linenum1
        << ". Second one starts at file " << loc -> filename1
        << ", line " << loc -> linenum1 << "." <<std::endl;
     else
      std::cerr 
        << "Function " << decl->getNameInfo().getAsString()
        << " has two specifications. The last one starts at file "
        << loc -> filename1
        << ", line " << loc -> linenum1 << "." <<std::endl;
     exit(2);
  }
  comment.parseFunctionContract(contract, decl, _context, _sema,
      _parents.getScope(), _clangUtils, _rttiTable);
}

void FramacVisitor::parseDefaultComment(AnnotationComment& comment) {
  clang::FileID beginFileId, endFileId;
  unsigned beginOffset, endOffset;
  std::tie(beginFileId, beginOffset) = _context->getSourceManager()
      .getDecomposedLoc(comment.getSourceRange().getBegin());
  std::tie(endFileId, endOffset) = _context->getSourceManager()
      .getDecomposedLoc(comment.getSourceRange().getEnd());
  std::ostringstream msg;
  if (const clang::FileEntry *F = _context->getSourceManager()
      .getFileEntryForID(beginFileId))
    msg << "Unknown kind of comment at " << F->getName().str() << ":"
      << _context->getSourceManager().getLineNumber(beginFileId,beginOffset)
      << ":"
      << _context->getSourceManager().getLineNumber(beginFileId,
          beginOffset) << ":"
      << _context->getSourceManager().getColumnNumber(beginFileId,
          beginOffset)
	    << _context->getSourceManager().getColumnNumber(endFileId,
		      endOffset); // Comments that begin in one file and end in another are already filtered out
  else
    msg << "Unknown kind of comment encountered";
  std::cerr <<  msg.str() << std::endl;
  exit(2);
}

void FramacVisitor::registerTemplateDecl(clang::Decl* Decl) {
  // const clang::CXXRecordDecl *RD
  //   = static_cast<const clang::CXXRecordDecl*>(Decl->getDeclContext());
  // if (RD && RD->getDescribedClassTemplate()
  //       && RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)

  while ((_commentIndex < (int) _annotationCommentList.size()-1)
      && _context->getSourceManager().isBeforeInTranslationUnit(
          _annotationCommentList[_commentIndex+1]->getSourceRange().getEnd(),
          Decl->getSourceRange().getBegin())) {
    AnnotationComment& comment = *_annotationCommentList[++_commentIndex];
    TemplatedAttachedComments::iterator found
      = _templatedAttachedComments.lower_bound(Decl);
    if (found == _templatedAttachedComments.end() || found->first != Decl)
      found = _templatedAttachedComments.insert(found,
          std::make_pair(Decl, std::list<AnnotationComment*>()));
    found->second.push_back(&comment);
  };
  // to uncomment if needed (see VisitTypedef)
  // _tableForWaitingDeclarations.addDeclaration(Decl, _globals);
  // _instanceContexts.removeUnvisited(Decl);
}

void FramacVisitor::registerAttachedNextTemplateDecl(clang::Decl* Decl) {
  // attach annotation comment to decl
  while ((_commentIndex < (int) _annotationCommentList.size()-1)
      && _context->getSourceManager().isBeforeInTranslationUnit(
          _annotationCommentList[_commentIndex+1]->getSourceRange().getEnd(),
          Decl->getSourceRange().getBegin())) {
    AnnotationComment& comment = *_annotationCommentList[++_commentIndex];
    if (comment.getKind() == AnnotationComment::KNext) {
      TemplatedAttachedComments::iterator
          found = _templatedAttachedComments.lower_bound(Decl);
      if (found == _templatedAttachedComments.end() || found->first != Decl)
        found = _templatedAttachedComments.insert(found,
            std::make_pair(Decl, std::list<AnnotationComment*>()));
      found->second.push_back(&comment);
    };
  };
}

void FramacVisitor::readFunctionComments(
  clang::FunctionDecl* Decl, int& nextPreviousComment, bool& isNewTemplate,
    std::list<AnnotationComment*>*& templatedNextAnnotations) {
  std::list<AnnotationComment*>* templatedComments = NULL;
  int previousComment = _commentIndex,
      previousTemplateComment = _templateCommentIndex;
  isNewTemplate = false;
  if (isTemplateInstance(Decl->getTemplateSpecializationKind())) {
    clang::Decl* primaryTemplate = Decl->getPrimaryTemplate();
    isNewTemplate = primaryTemplate;
    if (!isNewTemplate) {
      clang::MemberSpecializationInfo* memberInfo
        = Decl->getMemberSpecializationInfo();
      if (memberInfo)
        primaryTemplate = memberInfo->getInstantiatedFrom();
    };
    assert(primaryTemplate);
    { TemplatedAttachedComments::iterator found
          = _templatedAttachedComments.find(primaryTemplate);
      if (found != _templatedAttachedComments.end())
        templatedComments = &found->second;
    };
    // if (_templateCommentIndex >= -1)
    //   // for the moment, we don't know if we need a stack of instantiations
    //   _templateCommentIndex = -2;
    previousTemplateComment = _templateCommentIndex = findCommentAfterRange(
        primaryTemplate->getSourceRange());
  }
  else if (!Decl->isImplicit())
    advanceCommentUntil(Decl->getSourceRange().getBegin());

  templatedNextAnnotations = NULL;
  if (templatedComments) {
    std::list<AnnotationComment*>::iterator iterEnd = templatedComments->end();
    for (std::list<AnnotationComment*>::iterator iter
        = templatedComments->begin(); iter != iterEnd; ++iter) {
      switch ((*iter)->getKind()) {
        case AnnotationComment::KGlobal:
          // parseTemplatedGlobalComment(**iter, Decl->getDeclContext());
          //   find the template arguments in the instance stack
          //   if !primaryTemplate
          parseGlobalComment(**iter, Decl->getDeclContext());
          break;
        case AnnotationComment::KGhost:
          parseGhostGlobal(**iter, Decl->getDeclContext());
          break;
        case AnnotationComment::KNext:
          if (!templatedNextAnnotations)
            templatedNextAnnotations = new std::list<AnnotationComment*>();
          templatedNextAnnotations->push_back(*iter);
          break;
        default:
          parseDefaultComment(**iter);
          break;
      };
    };
  };
  
  while (previousTemplateComment < _templateCommentIndex) {
    ++previousTemplateComment;
    switch (_annotationCommentList[previousTemplateComment]->getKind()) {
      case AnnotationComment::KGlobal:
        parseGlobalComment(*_annotationCommentList[previousTemplateComment],
            Decl->getDeclContext());
        break;
      case AnnotationComment::KGhost:
          parseGhostGlobal(
            *_annotationCommentList[previousTemplateComment],
            Decl->getDeclContext());
          break;
      case AnnotationComment::KNext:
        // [TODO] for contracts.
        break;
      case AnnotationComment::KNextLoop:
      case AnnotationComment::KNextStatement:
        { std::cerr << "unimplemented next attachment" << std::endl;
          exit(2);
          assert(false);
          // Acsl::FunctionContract functionContract(Decl);
          // Acsl::Parser parser(Decl->getDeclContext(), _context, _sema,
          //    _parents.getScope(), functionContract);
          // parser.parse(*_annotationCommentList[previousTemplateComment]
          //    .getContent());
        };
        break;
      default:
        parseDefaultComment(*_annotationCommentList[previousTemplateComment]);
        break;
    };
  };

  nextPreviousComment = previousComment;

  while (previousComment < _commentIndex) {
    ++previousComment;
    switch (_annotationCommentList[previousComment]->getKind()) {
      case AnnotationComment::KGlobal:
        parseGlobalComment(*_annotationCommentList[previousComment],
            Decl->getDeclContext());
        break;
      case AnnotationComment::KGhost:
          parseGhostGlobal(*_annotationCommentList[previousComment],
                           Decl->getDeclContext());
          break;
      case AnnotationComment::KNext:
        break;
      default:
        parseDefaultComment(*_annotationCommentList[previousComment]);
        break;
    };
  };
}

void FramacVisitor::readContractComments(
  clang::FunctionDecl* Decl, option /* function_contract */& contract,
  bool hasPushedBody, bool& hasParseTemplate, int& nextPreviousComment,
  std::list<AnnotationComment*>* templatedNextAnnotations) {
  if (nextPreviousComment < _commentIndex) {
    if (!hasPushedBody)
      _parents.pushFunctionBody(Decl);
    do {
      ++nextPreviousComment;
      if (_annotationCommentList[nextPreviousComment]->getKind()
          == AnnotationComment::KNext) {
        parseFunctionContractComment(
          *_annotationCommentList[nextPreviousComment], contract, Decl);
      };
    } while (nextPreviousComment < _commentIndex);
    if (!hasPushedBody)
      _parents.popFunctionBody(Decl);
  };
  if (!hasParseTemplate && templatedNextAnnotations
        && !templatedNextAnnotations->empty()) {
    if (!hasPushedBody)
      _parents.pushFunctionBody(Decl);
    hasParseTemplate = true;
    std::list<AnnotationComment*>::iterator
      iterEnd = templatedNextAnnotations->end();
    for (std::list<AnnotationComment*>::iterator iter
        = templatedNextAnnotations->begin(); iter != iterEnd; ++iter)
      // the test can be removed in the case of templatedNextAnnotations
      // see discussion on commit 2c019698
      if ((*iter)->getKind() == AnnotationComment::KNext)
        parseFunctionContractComment(**iter, contract, Decl);
    if (!hasPushedBody)
      _parents.popFunctionBody(Decl);
  };
}

void FramacVisitor::readRecordComments(
  clang::RecordDecl* Decl, const clang::CXXRecordDecl *RD,
  const clang::ClassTemplateSpecializationDecl* TSD,
  bool& isDeportedInstantiation) {
  std::list<AnnotationComment*>* templatedComments = NULL;
  int previousComment = _commentIndex, previousTemplateComment
    = _templateCommentIndex, oldTemplateContext = _templateCommentIndex;
  if (RD /* || hasTemplateContext() */) {
    // [TODO] handle partial instantiation
    if (RD->getDescribedClassTemplate()
        && RD->getDescribedClassTemplate()->getTemplatedDecl()) {
      clang::CXXRecordDecl* primaryRecord
          = RD->getDescribedClassTemplate()->getTemplatedDecl();
      assert(primaryRecord);
      isDeportedInstantiation = true;
      { TemplatedAttachedComments::iterator
        found = _templatedAttachedComments.find(primaryRecord);
        if (found != _templatedAttachedComments.end())
          templatedComments = &found->second;
        // if (_templateCommentIndex >= -1)
        // // for the moment, we don't know if we need a stack of instantiations
        //   _templateCommentIndex = -2;
      };
      previousTemplateComment = _templateCommentIndex = findCommentAfterRange(
          primaryRecord->getSourceRange());
    };
  };

  if (RD && TSD && isTemplateInstance(RD->getTemplateSpecializationKind())
      /* RD->getTemplateSpecializationKind()
          >= clang::TSK_ImplicitInstantiation */) {
    // _parents.setVisitInstances();
    // assert(llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(RD));

    clang::CXXRecordDecl* primaryRecord
      = /* static_cast<const clang::ClassTemplateSpecializationDecl*>(RD) */
          TSD->getSpecializedTemplate()->getTemplatedDecl();
    // RD->getDescribedClassTemplate()->getTemplatedDecl();
    assert(primaryRecord);
    isDeportedInstantiation = true;
    { TemplatedAttachedComments::iterator
      found = _templatedAttachedComments.find(primaryRecord);
      if (found != _templatedAttachedComments.end())
        templatedComments = &found->second;
      // if (_templateCommentIndex >= -1)
      //   // for the moment, we don't know if we need a stack of instantiations
      //   _templateCommentIndex = -2;
    };
    previousTemplateComment = _templateCommentIndex = findCommentAfterRange(
        primaryRecord->getSourceRange());
  };
  if (!isDeportedInstantiation)
    advanceCommentUntil(Decl->getSourceRange().getBegin());

  if (templatedComments) {
    assert(RD);
    std::list<AnnotationComment*>::iterator iterEnd = templatedComments->end();
    for (std::list<AnnotationComment*>::iterator iter
        = templatedComments->begin(); iter != iterEnd; ++iter) {
      switch ((*iter)->getKind()) {
        case AnnotationComment::KGlobal:
          // parseTemplatedGlobalComment(**iter, Decl->getDeclContext());
          //   find the template arguments in the instance stack
          //   if !primaryTemplate
          parseGlobalComment(**iter, Decl->getDeclContext());
          break;
        case AnnotationComment::KGhost:
          parseGhostGlobal(**iter, Decl->getDeclContext());
          break;
        case AnnotationComment::KNext:
          if (RD) {
            std::cerr << "unimplemented next attachment" << std::endl;
            exit(2);
            assert(false);
            // Acsl::ClassContract classContract(RD);
            // Acsl::Parser parser(Decl->getDeclContext(), _context, _sema,
            //     _parents.getScope(), functionContract);
            // parser.parse(*_annotationCommentList[previousComment]
            //     .getContent());
            break;
          };
          break;
        default:
          parseDefaultComment(**iter);
          break;
      };
      std::cerr << "unimplemented template comment" << std::endl;
      exit(2);
      assert(false);
      // Acsl::ClassContract classContract(RD);
      // Acsl::Parser parser(Decl->getDeclContext(), _context, _sema,
      //     _parents.getScope(), classContract);
      // parser.parse(iter->getContent());
    };
  };
  
  while (previousTemplateComment < _templateCommentIndex) {
    ++previousTemplateComment;
    switch (_annotationCommentList[previousTemplateComment]->getKind()) {
    case AnnotationComment::KGlobal:
      parseGlobalComment(*_annotationCommentList[previousTemplateComment],
          Decl->getDeclContext());
      break;
    case AnnotationComment::KGhost:
          parseGhostGlobal(*_annotationCommentList[previousTemplateComment],
                           Decl->getDeclContext());
          break;
    case AnnotationComment::KNext:
        if (RD) { 
          std::cerr << "unimplemented next attachment" << std::endl;
          exit(2);
          assert(false);
          // Acsl::ClassContract classContract(RD);
          // Acsl::Parser parser(Decl->getDeclContext(), _context, _sema,
          //     _parents.getScope(), functionContract);
          // parser.parse(*_annotationCommentList[previousTemplateComment]
          //     .getContent());
          break;
        };
        break;
      default:
        parseDefaultComment(*_annotationCommentList[previousTemplateComment]);
        break;
    };
  };

  while (previousComment < _commentIndex) {
    ++previousComment;
    switch (_annotationCommentList[previousComment]->getKind()) {
      case AnnotationComment::KGlobal:
        parseGlobalComment(*_annotationCommentList[previousComment],
            Decl->getDeclContext());
        break;
      case AnnotationComment::KGhost:
          parseGhostGlobal(*_annotationCommentList[previousComment],
                           Decl->getDeclContext());
          break;
      case AnnotationComment::KNext:
        if (RD) {
          std::cerr << "unimplemented next attachment" << std::endl;
          exit(2);
          assert(false);
          // Acsl::ClassContract classContract(RD);
          // Acsl::Parser parser(Decl->getDeclContext(), _context, _sema,
          //     _parents.getScope(), functionContract);
          // parser.parse(*_annotationCommentList[previousComment]
          //     .getContent());
          break;
        };
        break;
      default:
        parseDefaultComment(*_annotationCommentList[previousComment]);
        break;
    };
  };
  if (RD && !RD->isThisDeclarationADefinition() && _templateCommentIndex >= -1
      && oldTemplateContext < -1)
   _templateCommentIndex = -2;
}

void FramacVisitor::readInnerRecordComments(clang::RecordDecl* Decl) {
  int previousTemplateComment = _templateCommentIndex;
  int previousComment = _commentIndex;
  advanceCommentUntil(Decl->getSourceRange().getEnd());
  while (previousTemplateComment < _templateCommentIndex) {
    ++previousTemplateComment;
    switch (_annotationCommentList[previousTemplateComment]->getKind()) {
      case AnnotationComment::KGlobal:
        parseGlobalComment(*_annotationCommentList[previousTemplateComment],
            Decl);
        break;
      case AnnotationComment::KGhost:
          parseGhostGlobal(*_annotationCommentList[previousTemplateComment],
                           Decl->getDeclContext());
          break;
      case AnnotationComment::KNext:
        { const clang::CXXRecordDecl *RD
            = llvm::dyn_cast<clang::CXXRecordDecl>(Decl);
          if (RD) { 
            std::cerr << "unimplemented next attachment" << std::endl;
            exit(2);
            assert(false);
            // Acsl::ClassContract classContract(RD);
            // Acsl::Parser parser(Decl->getDeclContext(), _context, _sema,
            //     _parents.getScope(), functionContract);
            // parser.parse(*_annotationCommentList[previousTemplateComment]
            //     .getContent());
          };
        };
        break;
      default:
        parseDefaultComment(*_annotationCommentList[previousTemplateComment]);
        break;
    };
  };

  while (previousComment < _commentIndex) {
    ++previousComment;
    switch (_annotationCommentList[previousComment]->getKind()) {
      case AnnotationComment::KGlobal:
        parseGlobalComment(*_annotationCommentList[previousComment], Decl);
        break;
      case AnnotationComment::KGhost:
          parseGhostGlobal(*_annotationCommentList[previousComment],
                           Decl->getDeclContext());
          break;
      case AnnotationComment::KNext:
        { const clang::CXXRecordDecl *RD
            = llvm::dyn_cast<clang::CXXRecordDecl>(Decl);
          if (RD) {
            std::cerr << "unimplemented next attachment" << std::endl;
            exit(2);
            assert(false);
            // Acsl::ClassContract classContract(RD);
            // Acsl::Parser parser(Decl->getDeclContext(), _context, _sema,
            //     _parents.getScope(), functionContract);
            // parser.parse(_annotationCommentList[previousComment]
            //     ->getContent());
          };
        };
        break;
      default:
        parseDefaultComment(*_annotationCommentList[previousComment]);
        break;
    };
  };
}

void FramacVisitor::readGlobalCommentUntil(
  clang::SourceLocation loc, clang::DeclContext* context, bool mayHaveTemplate) 
{
  int previousComment = _commentIndex;
  int previousTemplateComment = _templateCommentIndex;
  if (!mayHaveTemplate)
    assert(previousTemplateComment < -1);
  advanceCommentUntil(loc);
  if (mayHaveTemplate) {
    while (previousTemplateComment < _templateCommentIndex) {
      ++previousTemplateComment;
      switch (_annotationCommentList[previousTemplateComment]->getKind()) {
        case AnnotationComment::KGlobal:
          parseGlobalComment(
            *_annotationCommentList[previousTemplateComment], context);
          break;
        case AnnotationComment::KGhost:
          parseGhostGlobal(
            *_annotationCommentList[previousTemplateComment], context);
          break;
        default:
          parseDefaultComment(*_annotationCommentList[previousTemplateComment]);
          break;
      }
    };
  };
  while (previousComment < _commentIndex) {
    ++previousComment;
    switch (_annotationCommentList[previousComment]->getKind()) {
      case AnnotationComment::KGlobal:
        parseGlobalComment(
          *_annotationCommentList[previousComment], context);
        break;
      case AnnotationComment::KGhost:
          parseGhostGlobal(*_annotationCommentList[previousComment], context);
          break;
      default:
        parseDefaultComment(*_annotationCommentList[previousComment]);
        break;
    }
  };
}

void FramacVisitor::readGlobalComment(clang::Decl* Decl, bool mayHaveTemplate) {
  readGlobalCommentUntil(
    _clangUtils->getBeginLoc(*Decl), Decl->getDeclContext(), mayHaveTemplate);
}

/* bodyBare means that the class has virtual base classes; so the constructor
   should be duplicated with no calls to the virtual base class constructors */
void FramacVisitor::insertConstructorPreambleIn(
    const clang::CXXConstructorDecl& constructor,
    list& /*statement*/ bodyref, bool* shouldDelayInit,
    /* statement */ ForwardReferenceList* bodyBare) {
  clang::CXXConstructorDecl::init_const_reverse_iterator endInitIterator 
      = constructor.init_rend();
  /* insertionPvmtUpdatePoint = insertion position in the statement list:
       = &bodyref(->next)^n
     if some virtual methods appear the vmt initialization will be inserted
       between the statement cell containing *insertionPvmtUpdatePoint
       and **insertionPvmtUpdatePoint.
  */
  list* insertionPvmtUpdatePoint = NULL;
  list* insertionPvmtUpdateBarePoint = NULL;
  list* insertionPvmtUpdatePointBeforeBare = NULL;
  list body_head = bodyref;
  list body_bare_head = NULL;

  // According to 12.7, the initialization is done in the following
  // order:
  // Base classes initialization
  // Virtual Method Table initialization
  // Non-static member initialization
  // we build it in reverse, so that we can add statement in front of
  // the existing body.
  for (clang::CXXConstructorDecl::init_const_reverse_iterator initIterator
      = constructor.init_rbegin(); initIterator != endInitIterator;
      ++initIterator) {
    const clang::CXXCtorInitializer* initializer = *initIterator;
    assert(initializer);
    bool isBareField = false;
    bool isBaseVirtual = false;
    if (initializer->isBaseInitializer()) {
      clang::QualType baseType = initializer->getTypeSourceInfo()
        ->getType();
      variable thisVariable = variable_FunctionParameter("this");
      exp_node node = exp_node_Variable(thisVariable);
      location loc1 = makeLocation(constructor.getSourceRange());
      location loc2 = copy_loc(loc1);
      location loc3 = copy_loc(loc1);
      qual_type baseTypePointer =
        makeDefaultNameType(constructor.getLocation(), baseType);
      pkind pointerKind = pkind_PDataPointer(baseTypePointer);
      baseTypePointer = qual_type_cons(NULL, typ_Pointer(pointerKind));
      const clang::CXXRecordDecl* base = baseType.getTypePtr()
          ->getPointeeCXXRecordDecl();
      if (!base)
        base = baseType.getTypePtr()->getCanonicalTypeInternal().getTypePtr()
            ->getAsCXXRecordDecl();
      if (base) {
        if (hasInstanceContext()) {
          _tableForWaitingDeclarations.ensureGeneration(base, unvisitedDecls());
        }
        else if (shouldDelayInit && !*shouldDelayInit)
          *shouldDelayInit = !_tableForWaitingDeclarations.hasVisited(base);
      };

      isBaseVirtual = initializer->isBaseVirtual();
      if (base)
         isBareField = _rttiTable.hasVirtualBaseClasses(base);
      exp_node shiftNode = exp_node_PointerCast(baseTypePointer,
          reference_or_pointer_kind_RPKStaticBasePointer(),
          expression_cons(loc1,node));
      _implicitThisStar = expression_cons(loc3,
          exp_node_Dereference(expression_cons(loc2,shiftNode)));
      _isImplicitThisBare = isBareField;
      if (isBareField)
        insertionPvmtUpdatePointBeforeBare = &body_head;
    }
    else if (initializer->isAnyMemberInitializer()) {
      variable thisVariable = variable_FunctionParameter("this");
      exp_node node = exp_node_Variable(thisVariable);
      location loc1 = makeLocation(constructor.getSourceRange());
      location loc2 = copy_loc(loc1);
      location loc3 = copy_loc(loc1);
      clang::FieldDecl* field = initializer->getAnyMember();
      _memberType = field->getType()->getTypeClass();
      const clang::RecordDecl* base = field->getParent();
      if (base) {
        if (hasInstanceContext()) {
          _tableForWaitingDeclarations.ensureGeneration(base, unvisitedDecls());
        }
        else if (shouldDelayInit && !*shouldDelayInit)
          *shouldDelayInit = !_tableForWaitingDeclarations.hasVisited(base);
      };
      const char* field_name = _clangUtils->get_field_name(field);

      node = exp_node_Dereference(expression_cons(loc1,node));
      
      if (initializer->isIndirectMemberInitializer()) {
        clang::IndirectFieldDecl* indirect_fd =
          initializer->getIndirectMember() ;      
        
        for(auto it=indirect_fd->chain_begin();
            it+1 != indirect_fd->chain_end(); it++) {
          node =
            exp_node_FieldAccess(
              expression_cons(copy_loc(loc1), node),
              strdup(_clangUtils->findAnonymousName(*it).c_str()));
        }
      };
      
      _implicitThisStar=
        expression_cons(
          loc3,
          exp_node_FieldAccess(expression_cons(loc2, node), field_name));
      if(field->getType()->isReferenceType())
        _implicitThisStar =
          expression_cons(
            copy_loc(loc3),
            exp_node_Unary_operator(uokind_UOCastDerefInit(),_implicitThisStar));
    }
    else {
      std::cerr << "Unsupported constructor initializer:"
          << constructor.getDeclName().getAsString ()
          << "\nAborting\n";
      exit(2);
    };
    /* statement */ list temp = NULL;
    exp_node init;
    init = makeExpression(initializer->getInit(), shouldDelayInit, &temp);
    if (init) {
      statement initPart;
      if (!isBaseVirtual)
        initPart = statement_Expression(
            makeLocation(constructor.getSourceRange()),
            expression_cons(makeLocation(initializer->getInit()
                ->getSourceRange()), init));
      else
        initPart = statement_VirtualExpression(
            makeLocation(constructor.getSourceRange()),
            expression_cons(makeLocation(initializer->getInit()
                ->getSourceRange()), init));
      // if we are done with initializer of members, this is the place
      // where to insert initialization of virtual method table
      if (!_cleanups.empty()) {
        ForwardReferenceList header(temp);
        /* statement */ list* cursor = header.getBack()
          ? &header.getBack()->next : &header.getFront();
        header.insertContainer(initPart);
        makeCleanupsStmts(header, cursor, initPart->cons_statement.Expression
          .expr, constructor.getDeclContext(), NULL);
        if (initializer->isBaseInitializer() && !insertionPvmtUpdatePoint)
          insertionPvmtUpdatePoint = &header.getBack()->next;
        ForwardReferenceList tail(body_head);
        if (!isBareField && insertionPvmtUpdatePointBeforeBare == &body_head)
          insertionPvmtUpdatePointBeforeBare = &header.getBack()->next;
        header.append(tail);
        if (bodyBare && !isBaseVirtual) {
          list cursor = temp, endCursor = body_head;
          list tempBare = NULL;
          ForwardReferenceList headerBare(tempBare);
          while (cursor != endCursor) {
            assert(cursor != NULL);
            if (insertionPvmtUpdatePoint && *insertionPvmtUpdatePoint == cursor)
              insertionPvmtUpdateBarePoint = &headerBare.getBack()->next;
            headerBare.insertContainer(statement_dup((statement)
                cursor->element.container));
            cursor = cursor->next;
          };
          if (tempBare) {
            headerBare.getBack()->next = body_bare_head;
            body_bare_head = tempBare;
          };
        };
        body_head = temp;
      }
      else {
        bool doesNeedUpdate = !isBareField
            && insertionPvmtUpdatePointBeforeBare == &body_head;
        body_head = cons_container(initPart, body_head);
        if (doesNeedUpdate)
          insertionPvmtUpdatePointBeforeBare = &body_head->next;
        if (bodyBare && !isBaseVirtual)
          body_bare_head = cons_container(statement_dup(initPart),
            body_bare_head);
        if (initializer->isBaseInitializer() && !insertionPvmtUpdatePoint) {
          insertionPvmtUpdatePoint = &body_head->next;
          if (bodyBare && !isBaseVirtual)
            insertionPvmtUpdateBarePoint = &body_bare_head->next;
        }
        if(temp) {
          list t = temp;
          list tempBare = NULL;
          ForwardReferenceList headerBare(tempBare);
          if (bodyBare && !isBaseVirtual)
            headerBare.insertContainer(statement_dup((statement)
                t->element.container));
          while (t->next) {
            t = t->next;
            if (bodyBare && !isBaseVirtual)
              headerBare.insertContainer(statement_dup((statement)
                  t->element.container));
          };
          t->next = body_head;
          body_head = temp;
          if (bodyBare && !isBaseVirtual) {
            if (tempBare) {
              headerBare.getBack()->next = body_bare_head;
              body_bare_head = tempBare;
            };
          };
        }
      };
    }
    else {
      while (temp) {
        assert(false); // should not be called
        free_statement((statement) temp->element.container);
        list toBeFreed = temp;
        temp = temp->next;
        free(toBeFreed);
      };
    }
  };
  // update the body
  bodyref = body_head;
  if (bodyBare) {
    if (body_bare_head) {
      ForwardReferenceList first(body_bare_head);
      first.append(*bodyBare);
      bodyBare->getFront() = body_bare_head;
      if (!bodyBare->getBack())
        bodyBare->resetBack(first.getBack());
    };
    if (!insertionPvmtUpdateBarePoint)
      insertionPvmtUpdateBarePoint = &bodyBare->getFront();
    location loc = makeLocation(constructor.getSourceRange());
    _rttiTable.addBarePvmtSetter(*_clangUtils,
      const_cast<clang::CXXRecordDecl*>(constructor.getParent()),
      *insertionPvmtUpdateBarePoint, loc);
    free_location(loc);
  };
  // in case of a class with no inheritance, the first step is
  // to set up the virtual method table
  if (insertionPvmtUpdatePointBeforeBare
      && insertionPvmtUpdatePointBeforeBare != &body_head) {
    location loc = makeLocation(constructor.getSourceRange());
    _rttiTable.addPvmtSetterForBare(
      *_clangUtils,
      const_cast<clang::CXXRecordDecl*>(constructor.getParent()),
      *insertionPvmtUpdatePointBeforeBare,loc);
    free_location(loc);
  };

  if (!insertionPvmtUpdatePoint)
    insertionPvmtUpdatePoint = (list*) &bodyref;
  if (constructor.getParent() == _rttiTable.getCurrentClass())
    _rttiTable.addPvmtSetter(*insertionPvmtUpdatePoint);
  else if (_rttiTable.hasVirtualMethods(constructor.getParent())) {
    location loc = makeLocation(constructor.getSourceRange());
    // llvm::outs() << "constructor for class ";
    // constructor.getParent()->getNameForDiagnostic(llvm::outs(),
    //     _sema->getPrintingPolicy(),true);
    // llvm::outs() << " while current class is ";
    // _rttiTable.getCurrentClass()->getNameForDiagnostic(llvm::outs(),
    //     _sema->getPrintingPolicy(),true);
    // llvm::outs() << "\n";
    _rttiTable.addPvmtSetter(
      *_clangUtils,
      const_cast<clang::CXXRecordDecl*>(constructor.getParent()),
      *insertionPvmtUpdatePoint,loc);
    free_location(loc);
  };
}

/* bodyBare means that the class has virtual base classes; so the destructor 
   should be duplicated with no calls to the virtual base class destructors */
void FramacVisitor::insertDestructorPreambleIn(
    const clang::CXXDestructorDecl& destructor,
    option /* statement list */ bodyref,
    /* statement */ ForwardReferenceList* bodyBare) {
  const clang::CXXRecordDecl* parent = destructor.getParent();
  if (!parent)
    return;

  list* insertionPvmtUpdatePoint = (list*) &bodyref->content.container;
  list* insertionPvmtUpdateBarePoint = bodyBare ? &bodyBare->getFront() : NULL;
  list* insertionPvmtUpdatePointBeforeBare = NULL;
  std::vector<const clang::CXXRecordDecl*> virtualBases;
  if (parent == _rttiTable.getCurrentClass()) {
    _rttiTable.addPvmtSetter(*insertionPvmtUpdatePoint);
    virtualBases = _rttiTable.getCurrentClassInfo()->getVirtualBases();
    if (bodyBare && !virtualBases.empty()) {
      location loc = makeLocation(destructor.getSourceRange());
      _rttiTable.addBarePvmtSetter(*_clangUtils,
        const_cast<clang::CXXRecordDecl*>(parent),
        *insertionPvmtUpdateBarePoint,
        loc);
      free_location(loc);
    };
  }
  else if (_rttiTable.hasVirtualMethods(parent)) {
    location loc = makeLocation(destructor.getSourceRange());
    _rttiTable.addPvmtSetter(*_clangUtils,
      const_cast<clang::CXXRecordDecl*>(parent),
      *insertionPvmtUpdatePoint, loc);
    virtualBases = _rttiTable.getClassInfo(parent)->getVirtualBases();
    if (bodyBare && !virtualBases.empty())
      _rttiTable.addBarePvmtSetter(*_clangUtils,
        const_cast<clang::CXXRecordDecl*>(parent),
        *insertionPvmtUpdateBarePoint,
        loc);
    free_location(loc);
  };

  ForwardReferenceList body(*(list*) &bodyref->content.container);
  list body_head = NULL;
  clang::CXXRecordDecl::base_class_const_iterator endBase = parent->bases_end(),
      iterBase = parent->bases_begin();
  for (; iterBase != endBase; ++iterBase) {
    const clang::Type* baseType = iterBase->getType().getTypePtr();
    const clang::CXXRecordDecl* base = baseType->getAsCXXRecordDecl();
    if (!base)
      base = baseType->getCanonicalTypeInternal().getTypePtr()
          ->getAsCXXRecordDecl();
    if (!base || base->hasTrivialDestructor())
      continue;
    // if (hasInstanceContext()) {
    //   _tableForWaitingDeclarations.ensureGeneration(base,
    //       const_cast<Visitor*>(this)->unvisitedDecls());
    // }
    // else if (shouldDelayInit && !*shouldDelayInit)
    //   *shouldDelayInit = !_tableForWaitingDeclarations.hasVisited(base);

    variable thisVariable = variable_FunctionParameter("this");
    exp_node node = exp_node_Variable(thisVariable);
    location loc = makeLocation(destructor.getSourceRange());
    qual_type baseTypePointer = makeDefaultNameType(
      _clangUtils->getBeginLoc(*iterBase),iterBase->getType());
    pkind pointerKind = pkind_PDataPointer(baseTypePointer);
    baseTypePointer = qual_type_cons(NULL, typ_Pointer(pointerKind));

    bool isBaseVirtual = iterBase->isVirtual();
    exp_node shiftNode = NULL;
    bool isBareField = _rttiTable.hasVirtualBaseClasses(base);
    shiftNode = exp_node_PointerCast(baseTypePointer,
        reference_or_pointer_kind_RPKStaticBasePointer(),
        expression_cons(loc,node));
    if (isBaseVirtual) {
      std::vector<const clang::CXXRecordDecl*>::iterator
        iterEnd = virtualBases.end(), iter;
      for (iter = virtualBases.begin(); iter != iterEnd; ++iter)
        if (*iter == base) break;
      assert(iter != iterEnd);
      virtualBases.erase(iter);
    };

    qualified_name name
      = _clangUtils->makeQualifiedName(*base->getDestructor());
    if (isBareField)
      _rttiTable.addBareToQualification(name);
    list /* expression */ arguments
      = cons_container(expression_cons(copy_loc(loc),shiftNode), NULL);
    signature sig = _clangUtils->makeSignature(*base->getDestructor());
    expression call = expression_cons(copy_loc(loc),
      exp_node_Static_call(name, sig, funkind_FKDestructor(true),
        arguments, tkind_TStandard(), false));
    if (isBareField) {
      if (!body_head)
        insertionPvmtUpdatePointBeforeBare = &body_head;
      else {
        ForwardReferenceList tail(body_head);
        insertionPvmtUpdatePointBeforeBare = &tail.getBack()->next;
      };
    };

    statement initPart;
    if (!isBaseVirtual)
      initPart = statement_Expression(copy_loc(loc), call);
    else
      initPart = statement_VirtualExpression(copy_loc(loc), call);
    bool doesNeedUpdate = insertionPvmtUpdatePointBeforeBare == &body_head;
    body_head = cons_container(initPart, body_head);
    if (doesNeedUpdate)
      insertionPvmtUpdatePointBeforeBare = &body_head->next;
  };
  if (bodyBare) {
    list cursor = body_head;
    while (cursor) {
      bodyBare->insertContainer(statement_dup((statement)
          cursor->element.container));
      cursor = cursor->next;
    };
  }

  std::vector<const clang::CXXRecordDecl*>::const_reverse_iterator
    vendBase = virtualBases.rend(), viterBase = virtualBases.rbegin();
  for (; viterBase != vendBase; ++viterBase) {
    const clang::CXXRecordDecl* base = *viterBase;
    if (base->hasTrivialDestructor())
      continue;
    // if (hasInstanceContext()) {
    //   _tableForWaitingDeclarations.ensureGeneration(base,
    //       const_cast<Visitor*>(this)->unvisitedDecls());
    // }
    // else if (shouldDelayInit && !*shouldDelayInit)
    //   *shouldDelayInit = !_tableForWaitingDeclarations.hasVisited(base);

    variable thisVariable = variable_FunctionParameter("this");
    exp_node node = exp_node_Variable(thisVariable);
    location loc = makeLocation(destructor.getSourceRange());
    tkind base_kind = _clangUtils->makeTemplateKind(base);
    qual_type baseType = qual_type_cons(NULL,
      typ_Pointer(pkind_PDataPointer(qual_type_cons(NULL,
        typ_Struct(_clangUtils->makeQualifiedName(*base), base_kind)))));
    exp_node shiftNode = exp_node_PointerCast(baseType,
        reference_or_pointer_kind_RPKStaticBasePointer(),
        expression_cons(loc, node));

    qualified_name name
      = _clangUtils->makeQualifiedName(*base->getDestructor());
    if (_rttiTable.hasVirtualBaseClasses(base))
      _rttiTable.addBareToQualification(name);
    list /* expression */ arguments
      = cons_container(expression_cons(copy_loc(loc),shiftNode), NULL);
    signature sig = _clangUtils->makeSignature(*base->getDestructor());
    expression call = expression_cons(copy_loc(loc),
      exp_node_Static_call(name, sig, funkind_FKDestructor(true),
        arguments, tkind_TStandard(), false));
    body.insertContainer(statement_VirtualExpression(copy_loc(loc), call));
  };

  if (insertionPvmtUpdatePointBeforeBare) {
    location loc = makeLocation(destructor.getSourceRange());
    _rttiTable.addPvmtSetterForBare(*_clangUtils, parent,
      *insertionPvmtUpdatePointBeforeBare,loc);
    free_location(loc);
  };
  if (body_head) {
    if (body.getBack())
      body.getBack()->next = body_head;
    else
      bodyref->content.container = body_head;
  };
}

void FramacVisitor::addDelayedImplementation(
    translation_unit_decl delayedFunc, clang::FunctionDecl* Decl,
    const char* name, option /*statement list */& delayedBody,
    /* statement list */ option bodyBare /* may be NULL */) {
  if (delayedFunc->cons_translation_unit_decl.Function.fun_spec->is_some) {
    free_function_contract((function_contract) delayedFunc
      ->cons_translation_unit_decl.Function.fun_spec->content.container);
    free(delayedFunc->cons_translation_unit_decl.Function.fun_spec);
    delayedFunc->cons_translation_unit_decl.Function.fun_spec = opt_none();
  };
  free(delayedFunc->cons_translation_unit_decl.Function.body);
  delayedFunc->cons_translation_unit_decl.Function.body = delayedBody;
  delayedBody = NULL;
  if (delayedFunc->cons_translation_unit_decl.Function.fun_name
      ->tag_decl_or_impl_name == DECLARATION) {
    free_decl_or_impl_name(delayedFunc->cons_translation_unit_decl
          .Function.fun_name);
    qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
    free(const_cast<char*>(qual->decl_name));
    qual->decl_name = strdup(name);
    delayedFunc->cons_translation_unit_decl.Function.fun_name
      = decl_or_impl_name_Implementation(qual);
  };
  _delayedImplementations.insertContainer(delayedFunc);
  if (bodyBare) {
    qualified_name bareName = qualified_name_dup(delayedFunc
      ->cons_translation_unit_decl.Function.fun_name->cons_decl_or_impl_name
      .Implementation.name);
    _rttiTable.addBareToQualification(bareName);
    /* arg_decl */ list bareArgs = NULL;
    if (delayedFunc->cons_translation_unit_decl.Function.args) {
      list temp = delayedFunc->cons_translation_unit_decl.Function.args;
      ForwardReferenceList headerArgs(bareArgs);
      headerArgs.insertContainer(arg_decl_dup((arg_decl)
        temp->element.container));
      temp = temp->next;
      while (temp) {
        headerArgs.insertContainer(
          arg_decl_dup((arg_decl) temp->element.container));
        temp = temp->next;
      };
    }
    translation_unit_decl bareDeclFun = translation_unit_decl_Function(
      decl_or_impl_name_Implementation(bareName),
      funkind_dup(delayedFunc->cons_translation_unit_decl.Function.kind),
      copy_loc(delayedFunc->cons_translation_unit_decl.Function.fun_loc),
      qual_type_dup(delayedFunc->cons_translation_unit_decl.Function
        .return_type),
      bareArgs,
      bodyBare,
      delayedFunc->cons_translation_unit_decl.Function.is_extern_c,
      delayedFunc->cons_translation_unit_decl.Function.is_ghost,
      delayedFunc->cons_translation_unit_decl.Function.is_variadic,
      tkind_dup(delayedFunc->cons_translation_unit_decl.Function
          .template_kind),
      delayedFunc->cons_translation_unit_decl.Function.has_further_definition,
      opt_none() /* throws */,
      opt_none() /* spec */);
    _delayedImplementations.insertContainer(bareDeclFun);
  }
}

bool FramacVisitor::VisitFunctionDecl(clang::FunctionDecl* Decl) {
  if (!_ghost && isGhostDecl(Decl)) return true;

  // do not translate friend declaration: they're only there for
  // typechecking C++ and won't be used afterwards
  if (llvm::dyn_cast<clang::FriendDecl>(Decl)) return true;

  // Deleted functions are only useful in overload resolution.
  // We do not need them in the translation itself.
  if (Decl->isDeleted()) return true;

  if (/* _parents. */ hasTemplateContext() && _parents.isSemanticLocalClass())
    handleSemanticPostVisit(Decl->getDeclContext());
  else
    handlePostVisit(Decl->getLexicalDeclContext(), Decl->getDeclContext());
  if (hasTemplateContext())
  { registerTemplateDecl(Decl);
    return true;
  };
  if (Decl->getTemplatedKind() == clang::FunctionDecl::TK_FunctionTemplate) {
    registerAttachedNextTemplateDecl(Decl);
    return true;
  };


  int builtinID = Decl->getBuiltinID();
  if (builtinID > 0 && 
      !_context->BuiltinInfo.isPredefinedLibFunction(builtinID)) {
    // clang internal builtin or architecture specific function. Don't try
    // to visit it further.
    ensureBuiltinDeclaration(builtinID, Decl);
    _tableForWaitingDeclarations.addDeclaration(Decl, _globals);
    _instanceContexts.removeUnvisited(Decl);
    return true;
  };
  // Name
  // TODO: decompose the qualified name
  // Cf NestedNameSpecifier
  tkind templateExtension = NULL;
  const char* name = copy_string(Decl->getNameAsString ());
  if (Decl->getTemplateSpecializationKind() >= clang::TSK_ImplicitInstantiation)
  { clang::FunctionTemplateSpecializationInfo* info
      = Decl->getTemplateSpecializationInfo();
    if (info) {
      templateExtension =
        tkind_TTemplateInstance(_clangUtils->getTemplateExtension(info));
    };
  }
  if (!templateExtension)
    templateExtension = tkind_TStandard();

  int nextPreviousComment = _commentIndex;
  std::list<AnnotationComment*>* templatedNextAnnotations = NULL;
  bool isNewTemplate = false;
  readFunctionComments(Decl, nextPreviousComment, isNewTemplate,
      templatedNextAnnotations);

  bool isInstance = isTemplateInstance(
      Decl->getTemplateSpecializationKind());
  // Location
  location loc = makeLocation(Decl->getSourceRange());
  // Return type
  qual_type return_type =
    makeDefaultNameType(Decl->getLocation(), Decl->getReturnType());
  // Parameters
  list /*arg_decl*/ prms = NULL;
  pushInstanceFunction(); // arguments may depend from non-generated classes
  for (int i = Decl->getNumParams() - 1;i>=0;i--) {
    arg_decl cdecl = (arg_decl)malloc(sizeof(*cdecl));
    prms = cons_container(cdecl,prms);
    const clang::ParmVarDecl *prm = Decl->getParamDecl(i);
    if (prm->getNameAsString().size() > 0)
      cdecl->arg_name = copy_string(prm->getNameAsString());
    else {
      std::ostringstream out;
      out << "__frama_c_arg_" << i;
      cdecl->arg_name = copy_string(out.str());
    };
    cdecl->arg_type =
      makeDefaultNameType(prm->getLocation(), prm->getOriginalType());
    cdecl->arg_loc = makeLocation(prm->getSourceRange());
  }

  // Body
  option /*statement list */ body = opt_none();
  option /*statement list */ delayedBody = NULL;
  option /* function_contract */ contract = opt_none();
  clang::Decl::Kind declKind = Decl->getDeclContext()->getDeclKind();
  bool hasParseTemplate = false;
  bool isImplicitFunction = false;
  if (Decl->doesThisDeclarationHaveABody()) {
    _parents.pushFunctionBody(Decl);
    _typeFunctionResult = return_type;
    readContractComments(Decl, contract, true /* hasPushedBody */,
        hasParseTemplate, nextPreviousComment, templatedNextAnnotations);
    if (contract->is_some)
      _tableForWaitingDeclarations.visitFunctionDefinitionWithContract(Decl);
    // if (isInstance) {
      // sometimes template are before their use and the instantiation
      //   point needs some further declarations (isInstance).
      // sometimes template are after their use and the function use
      //   forward instance declarations (no way to characterize this fact).
      // pushInstanceFunction();
      if (isInstance && Decl->getPrimaryTemplate())
        _clangUtils->pushTemplateInstance(Decl->getPrimaryTemplate(),
            Decl->getTemplateSpecializationArgs());
    // };
    if (clang::CXXMethodDecl::classof(Decl)) {
      clang::CXXMethodDecl* meth = static_cast<clang::CXXMethodDecl*>(Decl);
      /* default assign operator is not defined correctly
        (returns *this instead of this) and others default
        methods seem to be undefined anyway. Just focus on the
        user-provided ones.
      */
      if (_clangUtils->doesGenerateImplicitMethods() || meth->isUserProvided())
      { bool shouldDelay = false;
        list /*<statement>*/ content = makeCodeBlock(Decl->getBody(),
            Decl->getDeclContext(), Decl,
            (declKind == clang::Decl::TranslationUnit
              || declKind == clang::Decl::LinkageSpec
              || declKind == clang::Decl::Namespace) ? NULL : &shouldDelay);
        if (!shouldDelay) {
          free(body);
          body = opt_some_container(content);
        }
        else
          delayedBody = opt_some_container(content);
      }
      else {
        isImplicitFunction = true;
        std::vector<const clang::Decl*> waitDeclarations;
        popInstanceFunction(waitDeclarations);
        if (isInstance && Decl->getPrimaryTemplate())
          _clangUtils->popTemplateInstance(Decl->getPrimaryTemplate());
      }
    }
    else {
      bool shouldDelay = false;
      list /*<statement>*/ content = makeCodeBlock(Decl->getBody(),
          Decl->getDeclContext(), Decl,
          (declKind == clang::Decl::TranslationUnit
            || declKind == clang::Decl::LinkageSpec
            || declKind == clang::Decl::Namespace) ? NULL : &shouldDelay);
      if (!shouldDelay) {
        free(body);
        body = opt_some_container(content);
      }
      else
        delayedBody = opt_some_container(content);
    };
    _typeFunctionResult = NULL;
    _parents.popFunctionBody(Decl);
  } else { // declaration only
    // just forget missing generation dependencies for arguments
    // when the body is missing
    std::vector<const clang::Decl*> waitDeclarations;
    popInstanceFunction(waitDeclarations);

    readContractComments(
      Decl, contract, false /* hasPushedBody */,
      hasParseTemplate, nextPreviousComment, templatedNextAnnotations);
    if (contract->is_some)
      _tableForWaitingDeclarations.visitFunctionDefinitionWithContract(Decl);
    if (templatedNextAnnotations)
      delete templatedNextAnnotations;
  }

  bool isGenerationEffective = true;
  if (declKind == clang::Decl::TranslationUnit
      || declKind == clang::Decl::LinkageSpec
      /* !_parents.hasLexicalContext() */) {
    assert(!delayedBody);
    decl_or_impl_name decl_name = NULL;
    if (Decl->getDeclContext() == Decl->getLexicalDeclContext())
      decl_name = decl_or_impl_name_Declaration(name);
    else {
      qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
      free(const_cast<char*>(qual->decl_name));
      qual->decl_name = name;
      decl_name = decl_or_impl_name_Implementation(qual);
    };
    translation_unit_decl func =
      translation_unit_decl_Function(
        decl_name, funkind_FKFunction(), loc, return_type, prms, body,
        Decl->isInExternCContext() /* || Decl->isExternCContext() */, _ghost,
        Decl->isVariadic(), templateExtension,
        false /* has_further_definition */, opt_none() /* throws */, contract);
    if (!contract->is_some) {
      _tableForWaitingDeclarations.addUpdateFunction(
        Decl,
        &func->cons_translation_unit_decl.Function.has_further_definition);
    };
    if (body->is_some /* && isInstance */) {
      std::vector<const clang::Decl*> waitDeclarations;
      popInstanceFunction(waitDeclarations);
      if (isInstance && Decl->getPrimaryTemplate())
        _clangUtils->popTemplateInstance(Decl->getPrimaryTemplate());
      if (!waitDeclarations.empty()) {
        isGenerationEffective = false;
        _tableForWaitingDeclarations.addIncompleteFunction(Decl,
            waitDeclarations, func);
      }
      else {
        _globals.insertContainer(func);
      };
    }
    else {
      _globals.insertContainer(func);
    }
  }
  else {
    if (Decl->getDeclContext()->isRecord() /* _parents.isClass() */) {
      const clang::CXXRecordDecl* current_class =
        llvm::cast<const clang::CXXRecordDecl>(Decl->getDeclContext());
      /* need to resynchronize RTTI table if the definition is outside of
         the class body. */
      option /* statement list */ bodyref = delayedBody ? delayedBody : body;
      option bodyBare = NULL;
      funkind kind;
      if (Decl->getCanonicalDecl()->getStorageClass() == clang::SC_Static ||
          Decl->getNameAsString() == "operator new"    ||
          Decl->getNameAsString() == "operator new[]"  ||
          Decl->getNameAsString() == "operator delete" ||
          Decl->getNameAsString() == "operator delete[]")
        kind = funkind_FKFunction();
      else {
        auto meth = llvm::cast<const clang::CXXMethodDecl>(Decl);
        if (meth->isVirtual()
            && Decl->getDeclContext() == Decl->getLexicalDeclContext())
          _rttiTable.addVirtualMethod(meth);
        if (llvm::isa<clang::CXXConstructorDecl>(Decl)) {
          kind = funkind_FKConstructor(true);
          if (strlen(name) == 0) {
            free(const_cast<char*>(name));
            name = strdup("constructor-special");
          };
          bool hasVirtualBaseClasses =
            _rttiTable.hasVirtualBaseClasses(current_class);
          if (bodyref->is_some) {
            bool shouldDelay = false;
            /* statement */ list bareResult = NULL;
            ForwardReferenceList headerBare(bareResult);
            if (hasVirtualBaseClasses &&
                _clangUtils->doesGenerateBareFunctions() &&
                (list) bodyref->content.container) {
              list cursor = (list) bodyref->content.container;
              headerBare.insertContainer((statement) cursor->element.container);
              cursor = cursor->next;
              while (cursor) {
                headerBare.insertContainer(
                  (statement) cursor->element.container);
                cursor = cursor->next;
              }
            }
            insertConstructorPreambleIn(
              *llvm::cast<const clang::CXXConstructorDecl>(Decl),
              *(list*)&bodyref->content.container, &shouldDelay,
              (hasVirtualBaseClasses&& _clangUtils->doesGenerateBareFunctions())
              ? &headerBare : NULL);
            if ((shouldDelay || (hasVirtualBaseClasses
                                 && _clangUtils->doesGenerateBareFunctions()))
                && !delayedBody) {
              delayedBody = body;
              body = opt_none();
            };
            if (bareResult)
              bodyBare = opt_some_container(bareResult);
          }
          else if (hasVirtualBaseClasses
                   && _clangUtils->doesGenerateBareFunctions())
            bodyBare = opt_none();
        } else if (llvm::isa<clang::CXXDestructorDecl>(Decl)) {
          // the same as for FKCONSTRUCTOR
          kind = funkind_FKDestructor(true);
          if (bodyref->is_some) {
            /* statement */ list bareResult = NULL;
            bool hasVirtualBaseClasses =
              _rttiTable.hasVirtualBaseClasses(current_class);
            ForwardReferenceList headerBare(bareResult);
            if (hasVirtualBaseClasses && (list) bodyref->content.container) {
              list cursor = (list) bodyref->content.container;
              headerBare.insertContainer((statement) cursor->element.container);
              cursor = cursor->next;
              while (cursor) {
                headerBare.insertContainer(
                  (statement) cursor->element.container);
                cursor = cursor->next;
              };
            };
            insertDestructorPreambleIn(
              *llvm::cast<const clang::CXXDestructorDecl>(Decl),
              bodyref,
              (hasVirtualBaseClasses &&
               _clangUtils->doesGenerateBareFunctions())
              ? &headerBare : NULL);
            if (hasVirtualBaseClasses &&
                _clangUtils->doesGenerateBareFunctions())
              { bodyBare = opt_some_container(bareResult);
                if (!delayedBody) {
                  delayedBody = body;
                  body = opt_none();
                };
              };
          }
          else if (_clangUtils->doesGenerateBareFunctions()
                   && _rttiTable.hasVirtualBaseClasses(current_class))
            bodyBare = opt_none();
        } else {
          kind = _clangUtils->cv_meth(meth);
        };
        arg_decl cdecl = (arg_decl)malloc(sizeof(*cdecl));
        prms = cons_container(cdecl,prms);
        cdecl->arg_name = copy_string("this");
        clang::QualType typeClass(current_class->getTypeForDecl(), 0);
        qual_type this_type =
          _clangUtils->makeType(Decl->getLocation(), typeClass);
        this_type->qualifier =
          cv_this_ptr(static_cast<clang::CXXMethodDecl*>(Decl));
        pkind pointerKind = pkind_PDataPointer(this_type);
        cdecl->arg_type = qual_type_cons(NULL, typ_Pointer(pointerKind));
        cdecl->arg_loc = makeLocation(Decl->getSourceRange());
      }
      bool is_implicit = isImplicitFunction || Decl->isDefaulted();
      bool is_variadic = Decl->isVariadic();
      if (_parents.isClass()) {
        if (body->is_some /* && isInstance */) {
          std::vector<const clang::Decl*> waitDeclarations;
          popInstanceFunction(waitDeclarations);
          if (isInstance && Decl->getPrimaryTemplate())
            _clangUtils->popTemplateInstance(Decl->getPrimaryTemplate());
          if (!waitDeclarations.empty()) {
            if (Decl->getDeclContext()->isRecord()) {
              assert(llvm::dyn_cast<clang::RecordDecl>(Decl->getDeclContext()));
              const clang::Decl* decl = static_cast<const clang::RecordDecl*>
                  (Decl->getDeclContext());
              for (int index = waitDeclarations.size()-1; index >= 0; --index) {
                const clang::Decl* waitingDecl = waitDeclarations[index];
                if (waitingDecl == decl || waitingDecl == Decl)
                  waitDeclarations.erase(waitDeclarations.begin() + index);
                else {
                  bool hasFound = false;
                  for (int i = 0; !hasFound && i < index; ++i)
                    hasFound = (waitingDecl == waitDeclarations[i]);
                  if (hasFound)
                    waitDeclarations.erase(waitDeclarations.begin() + index);
                };
              };
            };
          };
          if (!waitDeclarations.empty()) {
            qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
            free(const_cast<char*>(qual->decl_name));
            qual->decl_name = name;
            translation_unit_decl func =
              translation_unit_decl_Function(
                decl_or_impl_name_Implementation(qual), kind, loc, return_type,
                prms, body, false, _ghost,
                is_variadic, templateExtension,
                false /* has_further_definition */,
                opt_none () /* throws */,
                contract);
            translation_unit_decl funcBare = NULL;
            if (bodyBare) {
              decl_or_impl_name bareDecl =
                decl_or_impl_name_dup(func->cons_translation_unit_decl
                  .Function.fun_name);
              _rttiTable.addBareToQualification(bareDecl
                  ->cons_decl_or_impl_name.Implementation.name);
              list /*arg_decl*/ prmsBare = NULL;
              if (prms) {
                ForwardReferenceList headerPrmsCopy(prmsBare);
                headerPrmsCopy.insertContainer(arg_decl_dup((arg_decl)
                  prms->element.container));
                list tmp = prms->next;
                while (tmp) {
                  headerPrmsCopy.insertContainer(arg_decl_dup((arg_decl)
                    tmp->element.container));
                  tmp = tmp->next;
                };
              };
              funcBare = translation_unit_decl_Function(
                bareDecl,
                funkind_dup(kind), copy_loc(loc),
                qual_type_dup(return_type),
                prmsBare,
                body->is_some ? bodyBare : opt_none(),
                false, _ghost, is_variadic,
                tkind_dup(templateExtension),
                false /* has_further_definition */,
                opt_none () /* throws */,
                contract->is_some
                  ? opt_some_container(function_contract_dup((function_contract)
                      contract->content.container))
                  : opt_none());
            };
            if (Decl->getDeclContext()->isRecord()) {
              assert(llvm::dyn_cast<clang::RecordDecl>(Decl->getDeclContext()));
              const clang::RecordDecl* outerRecord = 
                static_cast<const clang::RecordDecl*>(Decl->getDeclContext());
              while (outerRecord) {
                _tableForWaitingDeclarations.ensureGeneration(outerRecord,
                  waitDeclarations);
                const clang::DeclContext* parentDecl = outerRecord->getParent();
                clang::Decl::Kind kindDecl = parentDecl->getDeclKind();
                if (kindDecl >= clang::Decl::firstRecord
                    && kindDecl <= clang::Decl::lastRecord) {
                  assert(llvm::dyn_cast<clang::RecordDecl>(parentDecl));
                  outerRecord = static_cast<const clang::RecordDecl*>(
                      parentDecl);
                }
                else
                  outerRecord = NULL;
              };
            };
            _tableForWaitingDeclarations.addIncompleteFunction(
              Decl, waitDeclarations, func, funcBare);
            isGenerationEffective = false;
          }
        }
        else if (delayedBody && delayedBody->is_some /* && isInstance */) {
          std::vector<const clang::Decl*> waitDeclarations;
          popInstanceFunction(waitDeclarations);
          if (isInstance && Decl->getPrimaryTemplate())
            _clangUtils->popTemplateInstance(Decl->getPrimaryTemplate());
        };
        if (isGenerationEffective) {
          class_decl method = class_decl_CMethod(loc, name, kind, return_type,
            prms, is_variadic, body, is_implicit, templateExtension,
            false /* has_further_definition */,
            opt_none() /* throws */,
            contract);
          if (!contract->is_some) {
            _tableForWaitingDeclarations.addUpdateFunction(
              Decl,
              &method->cons_class_decl.CMethod.has_further_definition);
          };
          bool doesDuplicateMethod = true;
          if (!_clangUtils->isExternCContext(Decl)) {
            _parents.add(method);
            if (bodyBare && !delayedBody) {
              list /*arg_decl*/ prmsCopy = NULL;
              if (prms) {
                ForwardReferenceList headerPrmsCopy(prmsCopy);
                headerPrmsCopy.insertContainer(arg_decl_dup((arg_decl)
                  prms->element.container));
                list tmp = prms->next;
                while (tmp) {
                  headerPrmsCopy.insertContainer(arg_decl_dup((arg_decl)
                    tmp->element.container));
                  tmp = tmp->next;
                };
              };
              class_decl methodBare = class_decl_CMethod(copy_loc(loc),
                strdup(name),
                funkind_dup(kind),
                qual_type_dup(return_type),
                prmsCopy,
                is_variadic, opt_none(),
                false /* is_implicit */, tkind_dup(templateExtension),
                false /* has_further_definition */,
                opt_none() /* throws */,
                contract->is_some
                  ? opt_some_container(function_contract_dup((function_contract)
                      contract->content.container))
                  : opt_none());
              translation_unit_decl funcBare =_tableForWaitingDeclarations
                .globalizeDecl(Decl, methodBare);
              decl_or_impl_name bareDecl =
                funcBare->cons_translation_unit_decl.Function.fun_name;
              _rttiTable.addBareToQualification(bareDecl
                ->cons_decl_or_impl_name.Implementation.name);
              addDelayedImplementation(funcBare, Decl, strdup(name),
                  bodyBare);
            };
          }
          else if (!delayedBody) {
            assert(!bodyBare);
            doesDuplicateMethod = false;
            delayedBody = body;
            method->cons_class_decl.CMethod.body = opt_none();
            body = NULL;
          };
          if (delayedBody) {
            class_decl method_definition = doesDuplicateMethod
              ? class_decl_dup(method) : method;
            method_definition->cons_class_decl.CMethod.is_implicit = false;
            assert(!bodyBare || doesDuplicateMethod);
            method_definition->cons_class_decl.CMethod.is_implicit
              = is_implicit && !delayedBody;
            addDelayedImplementation(_tableForWaitingDeclarations
              .globalizeDecl(Decl, method_definition), Decl, name, delayedBody,
                 bodyBare);
          };
        } // isGenerationEffective = false
        else {
          assert(!delayedBody);
          list /*arg_decl*/ copyPrms = NULL;
          list /*arg_decl*/* endCopyPrms = &copyPrms;
          list prmsCursor = prms;
          while (prmsCursor) {
            (*endCopyPrms) = cons_container(arg_decl_dup((arg_decl)
              prmsCursor->element.container), NULL);
            endCopyPrms = &(*endCopyPrms)->next;
            prmsCursor = prmsCursor->next;
          };
          class_decl method = class_decl_CMethod(copy_loc(loc),
            strdup(name), funkind_dup(kind), qual_type_dup(return_type),
            copyPrms, is_variadic, opt_none(), is_implicit,
            tkind_dup(templateExtension), false /* has_further_definition */,
            opt_none() /* throws */, opt_none());
          _parents.add(method);
          if (bodyBare) {
            list /*arg_decl*/ prmsBare = NULL;
            if (prms) {
              ForwardReferenceList headerPrmsCopy(prmsBare);
              headerPrmsCopy.insertContainer(arg_decl_dup((arg_decl)
                prms->element.container));
              list tmp = prms->next;
              while (tmp) {
                headerPrmsCopy.insertContainer(arg_decl_dup((arg_decl)
                  tmp->element.container));
                tmp = tmp->next;
              };
            };
            class_decl methodBare = class_decl_CMethod(copy_loc(loc),
              strdup(name),
              funkind_dup(kind),
              qual_type_dup(return_type),
              prmsBare,
              is_variadic, opt_none(),
              is_implicit, tkind_dup(templateExtension),
              false /* has_further_definition */,
              opt_none() /* throws */,
              opt_none());
            translation_unit_decl funcBare =_tableForWaitingDeclarations
              .globalizeDecl(Decl, methodBare);
            decl_or_impl_name bareDecl =
              funcBare->cons_translation_unit_decl.Function.fun_name;
            _rttiTable.addBareToQualification(bareDecl
              ->cons_decl_or_impl_name.Implementation.name);
            addDelayedImplementation(funcBare, Decl, strdup(name),
                bodyBare);
          };
        }
      }
      else {
        qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
        free(const_cast<char*>(qual->decl_name));
        qual->decl_name = name;
        decl_or_impl_name decl_name = decl_or_impl_name_Implementation(qual);
        if (bodyBare) {
          body = delayedBody;
          delayedBody = NULL;
        };
        translation_unit_decl func =
          translation_unit_decl_Function(
            decl_name, kind, loc, return_type, prms, body,
            false, _ghost, is_variadic,
            templateExtension,
            false /* has_further_definition */,
            opt_none() /*throws*/,
            contract);
        translation_unit_decl funcBare = NULL;
        if (bodyBare) {
          list /*arg_decl*/ prmsBare = NULL;
          if (prms) {
            ForwardReferenceList headerPrmsCopy(prmsBare);
            headerPrmsCopy.insertContainer(arg_decl_dup((arg_decl)
              prms->element.container));
            list tmp = prms->next;
            while (tmp) {
              headerPrmsCopy.insertContainer(arg_decl_dup((arg_decl)
                tmp->element.container));
              tmp = tmp->next;
            };
          };
          funcBare =
            translation_unit_decl_Function(
              decl_or_impl_name_dup(decl_name), funkind_dup(kind),
              copy_loc(loc), qual_type_dup(return_type), prmsBare, bodyBare,
              false, NULL, is_variadic,
              tkind_dup(templateExtension),
              false /* has_further_definition */,
              opt_none() /*throws*/,
              opt_none());
          decl_or_impl_name bareDecl =
            funcBare->cons_translation_unit_decl.Function.fun_name;
          _rttiTable.addBareToQualification(bareDecl
            ->cons_decl_or_impl_name.Implementation.name);
        };
        if (!contract->is_some) {
          _tableForWaitingDeclarations.addUpdateFunction(
            Decl,
            &func->cons_translation_unit_decl.Function.has_further_definition);
        };
        if (body->is_some /* && isInstance */) {
          std::vector<const clang::Decl*> waitDeclarations;
          popInstanceFunction(waitDeclarations);
          if (isInstance && Decl->getPrimaryTemplate())
            _clangUtils->popTemplateInstance(Decl->getPrimaryTemplate());
          if (!waitDeclarations.empty()) {
            if (!qual->prequalification) {
              qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
              free(const_cast<char*>(qual->decl_name));
              qual->decl_name = name;
              decl_name = decl_or_impl_name_Implementation(qual);
            };
            _tableForWaitingDeclarations.addIncompleteFunction(Decl,
                waitDeclarations, func);
            isGenerationEffective = false;
          }
        }
        else if (!body->is_some && Decl->getTemplateSpecializationKind()
              >= clang::TSK_ImplicitInstantiation
            && !_tableForWaitingDeclarations.hasGenerated(current_class)) {
          if (!qual->prequalification) {
            qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
            free(const_cast<char*>(qual->decl_name));
            qual->decl_name = name;
            decl_name = decl_or_impl_name_Implementation(qual);
          };
          std::vector<const clang::Decl*> waitDeclarations;
          _tableForWaitingDeclarations.ensureGeneration(current_class,
            waitDeclarations);
          _tableForWaitingDeclarations.addIncompleteFunction(Decl,
              waitDeclarations, func);
          isGenerationEffective = false;
        }
        else if (delayedBody && delayedBody->is_some /* && isInstance */) {
          std::vector<const clang::Decl*> waitDeclarations;
          popInstanceFunction(waitDeclarations);
          if (isInstance && Decl->getPrimaryTemplate())
            _clangUtils->popTemplateInstance(Decl->getPrimaryTemplate());
        };
        if (isGenerationEffective) {
          if (_parents.hasLexicalContext()) {
            if (funcBare)
              _parents.add(funcBare);
            _parents.add(func);
          }
          else {
            if (funcBare)
              _globals.insertContainer(funcBare);
            _globals.insertContainer(func);
          };
          if (delayedBody)
            addDelayedImplementation(translation_unit_decl_dup(func),
                Decl, name, delayedBody);
        }
        else
          assert(!delayedBody);
      };
    }
    else {
      assert(!delayedBody);
      assert(Decl->getDeclContext()->isNamespace() /*_parents.isNamespace() */);
      decl_or_impl_name decl_name = NULL;
      if (Decl->getDeclContext() == Decl->getLexicalDeclContext())
        decl_name = decl_or_impl_name_Declaration(name);
      else {
        qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
        free(const_cast<char*>(qual->decl_name));
        qual->decl_name = name;
        decl_name = decl_or_impl_name_Implementation(qual);
      };
      translation_unit_decl func =
        translation_unit_decl_Function(
          decl_name, funkind_FKFunction(), loc, return_type, prms, body,
          false, _ghost, Decl->isVariadic(), templateExtension,
          false /* has_further_definition */,
          opt_none() /* throws */,
          contract);
      if (!contract->is_some) {
        _tableForWaitingDeclarations.addUpdateFunction(
          Decl,
          &func->cons_translation_unit_decl.Function.has_further_definition);
      };
      if (body->is_some /* && isInstance */) {
        std::vector<const clang::Decl*> waitDeclarations;
        popInstanceFunction(waitDeclarations);
        if (isInstance && Decl->getPrimaryTemplate())
          _clangUtils->popTemplateInstance(Decl->getPrimaryTemplate());
        if (!waitDeclarations.empty()) {
          func->cons_translation_unit_decl.Function.body = opt_none();
          translation_unit_decl copyFunc = translation_unit_decl_dup(func);
          free(copyFunc->cons_translation_unit_decl.Function.body);
          copyFunc->cons_translation_unit_decl.Function.body = body;
          if (_parents.hasLexicalContext() && !_parents.isClass())
            _parents.add(func);
          else {
            _globals.insertContainer(func);
          };
          if (decl_name->tag_decl_or_impl_name == DECLARATION) {
            decl_name->cons_decl_or_impl_name.Declaration.name = strdup("");
            qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
            free(const_cast<char*>(qual->decl_name));
            qual->decl_name = name;
            free_decl_or_impl_name(decl_name);
            decl_name = decl_or_impl_name_Implementation(qual);
          };
          _tableForWaitingDeclarations.addIncompleteFunction(Decl,
              waitDeclarations, copyFunc);
          isGenerationEffective = false;
        }
      };
      if (isGenerationEffective) {
        if (_parents.hasLexicalContext() && !_parents.isClass()
          /* to avoid instantiation of friend functions */)
          _parents.add(func);
        else {
          _globals.insertContainer(func);
        };
      };
    };
  };
  // _parents.clearLocalPush();

  // comment the test if needed (see VisitTypedef)
  if (isGenerationEffective) {
    if (_parents.hasLexicalContext())
      _tableForWaitingDeclarations.addFunctionDeclaration(Decl, _globals);
    else
      _tableForWaitingDeclarations.addDeclaration(Decl, _globals);
    _instanceContexts.removeUnvisited(Decl);
  };
  if (isNewTemplate && _templateCommentIndex >= -1)
    _templateCommentIndex = -2;

  return true;
}

void FramacVisitor::handleLexicalPostVisit(
  const clang::DeclContext* currentContext) {
  int templateContextsNumber = _templateContextsNumber;
  if (currentContext && currentContext != _lexicalContextForPostVisit) {
    const clang::DeclContext* newContext = NULL;
    if (currentContext->isNamespace() || currentContext->isRecord())
      newContext = currentContext;
    int currentDepth = 0;
    const clang::DeclContext* iteratorContext = currentContext;
    while (iteratorContext && iteratorContext != _lexicalContextForPostVisit) {
      if (!newContext && (iteratorContext->isNamespace()
            || iteratorContext->isRecord()))
        newContext = iteratorContext;
      iteratorContext = iteratorContext->getLexicalParent();
      ++currentDepth;
    };
    if (iteratorContext == _lexicalContextForPostVisit) {
      if (newContext && newContext != _lexicalContextForPostVisit) {
        while (newContext != _lexicalContextForPostVisit) {
          const clang::DeclContext* lastContext = newContext;
          iteratorContext = newContext;
          while (iteratorContext != _lexicalContextForPostVisit) {
            if (iteratorContext->isNamespace() || iteratorContext->isRecord())
              lastContext = iteratorContext;
            iteratorContext = iteratorContext->getLexicalParent();
          };
          if (lastContext->isNamespace()) {
            assert(llvm::dyn_cast<clang::NamespaceDecl>(lastContext));
            localLexicalVisitNamespaceDecl(static_cast<clang::NamespaceDecl*>(
                const_cast<clang::DeclContext*>(lastContext)));
          }
          else if (lastContext->isRecord()) {
            assert(llvm::dyn_cast<clang::RecordDecl>(lastContext));
            localLexicalVisitRecordDecl(static_cast<clang::RecordDecl*>(
                const_cast<clang::DeclContext*>(lastContext)));
          };
          _lexicalContextForPostVisit = lastContext;
          clang::Decl::Kind declKind
              = _lexicalContextForPostVisit->getDeclKind();
          if (((declKind == clang::Decl::Namespace)
                || (declKind == clang::Decl::LinkageSpec)
                || (declKind == clang::Decl::TranslationUnit))
              && _delayedImplementations.getFront())
            _globals.append(_delayedImplementations);
        }
      };
      return;
    };
    int synchroDepth = 0;
    const clang::DeclContext* synchroContext = _lexicalContextForPostVisit;
    while (synchroContext != NULL) {
      synchroContext = synchroContext->getLexicalParent();
      ++synchroDepth;
    };
    while (currentDepth > synchroDepth) {
      if (!newContext && (currentContext->isNamespace()
            || currentContext->isRecord()))
        newContext = currentContext;
      currentContext = currentContext->getLexicalParent();
      --currentDepth;
    };
    while (synchroDepth > currentDepth) {
      if (_lexicalContextForPostVisit->isNamespace()) {
        assert(llvm::dyn_cast<clang::NamespaceDecl>(
              _lexicalContextForPostVisit));
        postLexicalVisitNamespaceDecl(static_cast<clang::NamespaceDecl*>(
              const_cast<clang::DeclContext*>(_lexicalContextForPostVisit)));
      }
      else if (_lexicalContextForPostVisit->isRecord()) {
        assert(llvm::dyn_cast<clang::RecordDecl>(_lexicalContextForPostVisit));
        postLexicalVisitRecordDecl(static_cast<clang::RecordDecl*>(
            const_cast<clang::DeclContext*>(_lexicalContextForPostVisit)),
            templateContextsNumber);
      };
      --synchroDepth;
      _lexicalContextForPostVisit = _lexicalContextForPostVisit
          ->getLexicalParent();
      clang::Decl::Kind declKind = _lexicalContextForPostVisit->getDeclKind();
      if (((declKind == clang::Decl::Namespace)
            || (declKind == clang::Decl::LinkageSpec)
            || (declKind == clang::Decl::TranslationUnit))
          && _delayedImplementations.getFront())
        _globals.append(_delayedImplementations);
    };
    while (currentContext != _lexicalContextForPostVisit) {
      if (!newContext && (currentContext->isNamespace()
            || currentContext->isRecord()))
        newContext = currentContext;
      if (_lexicalContextForPostVisit->isNamespace()) {
        assert(llvm::dyn_cast<clang::NamespaceDecl>
            (_lexicalContextForPostVisit));
        postLexicalVisitNamespaceDecl(static_cast<clang::NamespaceDecl*>(
              const_cast<clang::DeclContext*>(_lexicalContextForPostVisit)));
      }
      else if (_lexicalContextForPostVisit->isRecord()) {
        assert(llvm::dyn_cast<clang::RecordDecl>(_lexicalContextForPostVisit));
        postLexicalVisitRecordDecl(static_cast<clang::RecordDecl*>(
            const_cast<clang::DeclContext*>(_lexicalContextForPostVisit)),
            templateContextsNumber);
      };
      currentContext = currentContext->getLexicalParent();
      _lexicalContextForPostVisit = _lexicalContextForPostVisit->getLexicalParent();
      clang::Decl::Kind declKind = _lexicalContextForPostVisit->getDeclKind();
      if (((declKind == clang::Decl::Namespace)
            || (declKind == clang::Decl::LinkageSpec)
            || (declKind == clang::Decl::TranslationUnit))
          && _delayedImplementations.getFront())
        _globals.append(_delayedImplementations);
    };
    if (newContext && !_lexicalContextForPostVisit->isExternCContext()) {
      // _lexicalContextForPostVisit == newContext;
      while (newContext != _lexicalContextForPostVisit) {
        const clang::DeclContext* lastContext = newContext;
        iteratorContext = newContext;
        while (iteratorContext != _lexicalContextForPostVisit) {
          if (iteratorContext->isNamespace() || iteratorContext->isRecord())
            lastContext = iteratorContext;
          iteratorContext = iteratorContext->getLexicalParent();
        };
        if (lastContext->isNamespace()) {
          assert(llvm::dyn_cast<clang::NamespaceDecl>(lastContext));
          localLexicalVisitNamespaceDecl(static_cast<clang::NamespaceDecl*>(
              const_cast<clang::DeclContext*>(lastContext)));
        }
        else if (lastContext->isRecord()) {
          assert(llvm::dyn_cast<clang::RecordDecl>(lastContext));
          localLexicalVisitRecordDecl(static_cast<clang::RecordDecl*>(
              const_cast<clang::DeclContext*>(lastContext)));
        };
        _lexicalContextForPostVisit = lastContext;
        clang::Decl::Kind declKind = _lexicalContextForPostVisit->getDeclKind();
        if (((declKind == clang::Decl::Namespace)
              || (declKind == clang::Decl::LinkageSpec)
              || (declKind == clang::Decl::TranslationUnit))
            && _delayedImplementations.getFront())
          _globals.append(_delayedImplementations);
      }
    }
  };
}

void FramacVisitor::handleSemanticPostVisit(
  const clang::DeclContext* currentContext) {
  if (currentContext && currentContext != _semanticContextForPostVisit) {
    const clang::DeclContext* newContext = NULL;
    if (currentContext->isNamespace() || currentContext->isRecord())
      newContext = currentContext;
    int currentDepth = 0;
    const clang::DeclContext* iteratorContext = currentContext;
    while (iteratorContext && iteratorContext != _semanticContextForPostVisit) {
      if (!newContext && (iteratorContext->isNamespace()
            || iteratorContext->isRecord()))
        newContext = iteratorContext;
      iteratorContext = iteratorContext->getParent();
      ++currentDepth;
    };
    if (iteratorContext == _semanticContextForPostVisit) {
      if (newContext && newContext != _semanticContextForPostVisit) {
        while (newContext != _semanticContextForPostVisit) {
          const clang::DeclContext* lastContext = newContext;
          iteratorContext = newContext;
          while (iteratorContext != _semanticContextForPostVisit) {
            if (iteratorContext->isNamespace() || iteratorContext->isRecord())
              lastContext = iteratorContext;
            iteratorContext = iteratorContext->getParent();
          };
          if (lastContext->isNamespace()) {
            assert(llvm::dyn_cast<clang::NamespaceDecl>(lastContext));
            localSemanticVisitNamespaceDecl(static_cast<clang::NamespaceDecl*>(
                const_cast<clang::DeclContext*>(lastContext)));
          }
          else if (lastContext->isRecord()) {
            assert(llvm::dyn_cast<clang::RecordDecl>(lastContext));
            const clang::CXXRecordDecl *RD
              = llvm::dyn_cast<clang::CXXRecordDecl>(lastContext);
            if (RD && (RD->getDescribedClassTemplate()
                && (RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)))
            { pushTemplateContext();
              _state = (State) (_state | STemplateSynchronized);
            }
            else if (RD && (RD->getKind()
                    == clang::Decl::ClassTemplatePartialSpecialization)) {
              pushTemplateContext();
              _state = (State) (_state | STemplateSynchronized);
            };
            localSemanticVisitRecordDecl(static_cast<clang::RecordDecl*>(
                const_cast<clang::DeclContext*>(lastContext)));
          };
          _semanticContextForPostVisit = lastContext;
        }
      };
      return;
    };
    int synchroDepth = 0;
    const clang::DeclContext* synchroContext = _semanticContextForPostVisit;
    while (synchroContext != NULL) {
      synchroContext = synchroContext->getParent();
      ++synchroDepth;
    };
    while (currentDepth > synchroDepth) {
      if (!newContext && (currentContext->isNamespace()
            || currentContext->isRecord()))
        newContext = currentContext;
      currentContext = currentContext->getParent();
      --currentDepth;
    };
    while (synchroDepth > currentDepth) {
      if (_semanticContextForPostVisit->isNamespace()) {
        assert(llvm::dyn_cast<clang::NamespaceDecl>(
              _semanticContextForPostVisit));
        postSemanticVisitNamespaceDecl(static_cast<clang::NamespaceDecl*>(
              const_cast<clang::DeclContext*>(_semanticContextForPostVisit)));
      }
      else if (_semanticContextForPostVisit->isRecord()) {
        assert(llvm::dyn_cast<clang::RecordDecl>(_semanticContextForPostVisit));
        const clang::CXXRecordDecl *RD
          = llvm::dyn_cast<clang::CXXRecordDecl>(_semanticContextForPostVisit);
        if (RD && RD->getDescribedClassTemplate()
            && (RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)) {
          if (RD->getDescribedClassTemplate()->isThisDeclarationADefinition()
              || _parents.isSemanticLocalClass()) {
            assert(_state >= STemplateSynchronized);
            popTemplateContext();
            // done by postSemanticVisitRecordDecl
            // if (!hasTemplateContext()) {
            //   _state = (State) (_state & 0x3);
            //   if (hasInstanceContext())
            //     _state = (State) (_state | SInstanceSynchronized);
            // };
          }
        }
        else if (RD
            && RD->getKind() == clang::Decl::ClassTemplatePartialSpecialization
            && (RD->isThisDeclarationADefinition()
                || _parents.isSemanticLocalClass())) {
          assert(_state >= STemplateSynchronized);
          popTemplateContext();
          // done by postSemanticVisitRecordDecl
          // if (!hasTemplateContext()) {
          //   _state = (State) (_state & 0x3);
          //   if (hasInstanceContext())
          //     _state = (State) (_state | SInstanceSynchronized);
          // }
        }
        else {
          clang::Decl::Kind declKind = RD->getDeclContext()->getDeclKind();
          if ((declKind >= clang::Decl::firstFunction
                && declKind <= clang::Decl::lastFunction)) {
            if (static_cast<const clang::FunctionDecl*>(RD->getDeclContext())
                ->getTemplatedKind()==clang::FunctionDecl::TK_FunctionTemplate)
            { assert(_state >= STemplateSynchronized);
              popTemplateContext();
              // done by postSemanticVisitRecordDecl
              // if (!hasTemplateContext()) {
              //   _state = (State) (_state & 0x3);
              //   if (hasInstanceContext())
              //     _state = (State) (_state | SInstanceSynchronized);
              // };
            }
          };
        };

        // else if (RD && RD->getKind() // done by postSemanticVisitRecordDecl!
        //     == clang::Decl::ClassTemplateSpecialization) {
        //   // do not handle partial specialization !
        //   const clang::ClassTemplateSpecializationDecl* TSD
        //     = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(RD);
        //   if (TSD && TSD->isThisDeclarationADefinition() &&
        //       isTemplateInstance(RD->getTemplateSpecializationKind())) {
        //     assert(_state >= SInstanceSynchronized);
        //     popInstanceContext(RD);
        //     if (!hasInstanceContext())
        //       _state = (State) (_state & 0x3);
        //     _clangUtils->popTemplateInstance(TSD->getSpecializedTemplate());
        //   }
        // };
        postSemanticVisitRecordDecl(static_cast<clang::RecordDecl*>(
            const_cast<clang::DeclContext*>(_semanticContextForPostVisit)));
      };
      --synchroDepth;
      _semanticContextForPostVisit = _semanticContextForPostVisit->getParent();
    };
    while (currentContext != _semanticContextForPostVisit) {
      if (!newContext && (currentContext->isNamespace()
            || currentContext->isRecord()))
        newContext = currentContext;
      if (_semanticContextForPostVisit->isNamespace()) {
        assert(llvm::dyn_cast<clang::NamespaceDecl>
            (_semanticContextForPostVisit));
        postSemanticVisitNamespaceDecl(static_cast<clang::NamespaceDecl*>(
              const_cast<clang::DeclContext*>(_semanticContextForPostVisit)));
      }
      else if (_semanticContextForPostVisit->isRecord()) {
        assert(llvm::dyn_cast<clang::RecordDecl>(_semanticContextForPostVisit));
        const clang::CXXRecordDecl *RD
          = llvm::dyn_cast<clang::CXXRecordDecl>(_semanticContextForPostVisit);
        if (RD && RD->getDescribedClassTemplate()
            && (RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)) {
          if (RD->getDescribedClassTemplate()->isThisDeclarationADefinition()
              || _parents.isSemanticLocalClass()) {
            assert(_state >= STemplateSynchronized);
            popTemplateContext();
            // done by postSemanticVisitRecordDecl
            // if (!hasTemplateContext()) {
            //   _state = (State) (_state & 0x3);
            //   if (hasInstanceContext())
            //     _state = (State) (_state | SInstanceSynchronized);
            // };
          }
        }
        else if (RD
            && RD->getKind() == clang::Decl::ClassTemplatePartialSpecialization
            && (RD->isThisDeclarationADefinition()
                || _parents.isSemanticLocalClass())) {
          assert(_state >= STemplateSynchronized);
          popTemplateContext();
          // done by postSemanticVisitRecordDecl
          // if (!hasTemplateContext()) {
          //   _state = (State) (_state & 0x3);
          //   if (hasInstanceContext())
          //     _state = (State) (_state | SInstanceSynchronized);
          // }
        }
        // else if (RD && RD->getKind()
        //     == clang::Decl::ClassTemplateSpecialization) {
        //   // do not handle partial specialization !
        //   const clang::ClassTemplateSpecializationDecl* TSD
        //     = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(RD);
        //   if (TSD && TSD->isThisDeclarationADefinition() &&
        //       isTemplateInstance(RD->getTemplateSpecializationKind())) {
        //     assert(_state >= SInstanceSynchronized);
        //     popInstanceContext(RD);
        //     if (!hasInstanceContext())
        //       _state = (State) (_state & 0x3);
        //     _clangUtils->popTemplateInstance(TSD->getSpecializedTemplate());
        //   }
        // };
        postSemanticVisitRecordDecl(static_cast<clang::RecordDecl*>(
            const_cast<clang::DeclContext*>(_semanticContextForPostVisit)));
      };
      currentContext = currentContext->getParent();
      _semanticContextForPostVisit = _semanticContextForPostVisit->getParent();
    };
    if (newContext && !_semanticContextForPostVisit->isExternCContext()) {
      // _semanticContextForPostVisit == newContext;
      while (newContext != _semanticContextForPostVisit) {
        const clang::DeclContext* lastContext = newContext;
        iteratorContext = newContext;
        while (iteratorContext != _semanticContextForPostVisit) {
          if (iteratorContext->isNamespace() || iteratorContext->isRecord())
            lastContext = iteratorContext;
          iteratorContext = iteratorContext->getParent();
        };
        if (lastContext->isNamespace()) {
          assert(llvm::dyn_cast<clang::NamespaceDecl>(lastContext));
          localSemanticVisitNamespaceDecl(static_cast<clang::NamespaceDecl*>(
              const_cast<clang::DeclContext*>(lastContext)));
        }
        else if (lastContext->isRecord()) {
          assert(llvm::dyn_cast<clang::RecordDecl>(lastContext));
          const clang::CXXRecordDecl *RD
            = llvm::dyn_cast<clang::CXXRecordDecl>(lastContext);
          if (RD && RD->getDescribedClassTemplate()
              && (RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)) {
            pushTemplateContext();
            _state = (State) (_state | STemplateSynchronized);
          }
          else if (RD && (RD->getKind()
                  == clang::Decl::ClassTemplatePartialSpecialization)) {
            pushTemplateContext();
            _state = (State) (_state | STemplateSynchronized);
          }
          localSemanticVisitRecordDecl(static_cast<clang::RecordDecl*>(
              const_cast<clang::DeclContext*>(lastContext)));
        };
        _semanticContextForPostVisit = lastContext;
      }
    }
  };
}

bool FramacVisitor::localLexicalVisitNamespaceDecl(clang::NamespaceDecl* Decl)
{ // assert(false); // it occurs with specialization outside namespace
  const char* name;
  if (!_parents.hasLexicalContext())
    name = copy_string(Decl->getQualifiedNameAsString());
  else
    name = copy_string(Decl->getName().str());
  location loc = makeLocation(Decl->getSourceRange());
  translation_unit_decl namespaceDecl =
    translation_unit_decl_Namespace(loc, name, NULL);
  if (!_parents.hasLexicalContext()) {
    _globals.insertContainer(namespaceDecl);
  }
  else {
    assert(_parents.isNamespace());
    _parents.add(namespaceDecl);
  };
  assert(_state == SSynchronized);
  _parents.pushNamespace(
    namespaceDecl->cons_translation_unit_decl.Namespace.body, Decl);
  // _parents.pushLexicalLocalNamespace(Decl);
  return true;
}

bool FramacVisitor::localSemanticVisitNamespaceDecl(clang::NamespaceDecl* Decl)
{ assert(_state <= STempLexicalOut);
  _parents.pushSemanticLocalNamespace(Decl);
  // _state = STempLexicalOut;
  _semanticContextForPostVisit = Decl;
  return true;
}

bool FramacVisitor::TraverseLinkageSpecDecl(clang::LinkageSpecDecl* Decl) {
  bool res = Parent::TraverseLinkageSpecDecl(Decl);
  handlePostVisit(Decl, Decl);
  readGlobalCommentUntil(_clangUtils->getEndLoc(*Decl), Decl, false);
  return res;
}

bool FramacVisitor::TraverseNamespaceDecl(clang::NamespaceDecl* Decl) {
  bool res = Parent::TraverseNamespaceDecl(Decl);
  handlePostVisit(Decl->getLexicalDeclContext(), Decl->getParent());
  readGlobalCommentUntil(_clangUtils->getEndLoc(*Decl), Decl, false);
  return res;
}

bool FramacVisitor::VisitNamespaceDecl(clang::NamespaceDecl* Decl) {
  if (!_ghost && isGhostDecl(Decl)) return true;
  handlePostVisit(Decl->getLexicalDeclContext(), Decl->getParent());
  _lexicalContextForPostVisit = Decl;
  _semanticContextForPostVisit = Decl;
  readGlobalComment(Decl, false /* mayHaveTemplate */);
  const char* name;
  if (!_parents.hasLexicalContext())
    name = copy_string(Decl->getQualifiedNameAsString());
  else
    name = copy_string(Decl->getName().str());
  location loc = makeLocation(Decl->getSourceRange());
  translation_unit_decl namespaceDecl =
    translation_unit_decl_Namespace(loc, name, NULL);
  if (!_parents.hasLexicalContext()) _globals.insertContainer(namespaceDecl);
  else {
    assert(_parents.isNamespace());
    _parents.add(namespaceDecl);
  };
  assert(_state == SSynchronized);
  _parents.pushNamespace(
    namespaceDecl->cons_translation_unit_decl.Namespace.body, Decl);
  return true;
}

void FramacVisitor::postLexicalVisitNamespaceDecl(clang::NamespaceDecl* Decl) {
  if (!_parents.hasLexicalLocalContext(Decl))
    readGlobalComment(Decl, false /* mayHaveTemplate */);
  // assert(_state == SSynchronized);
  // the case STempLexicalOut occurs because postSemanticVisitNamespaceDecl
  //   has not still been called.
  _parents.lexicalSynchronizeWith(Decl->getLexicalDeclContext(), *this);
}

void FramacVisitor::postSemanticVisitNamespaceDecl(clang::NamespaceDecl* Decl) {
  // assert(_state == SSynchronized);
  // some local states may be in inner unsynchronized classes.
  _parents.semanticSynchronizeWith(Decl->getParent(), *this);
  assert(_state == SSynchronized);
}

bool FramacVisitor::VisitEnumDecl(clang::EnumDecl* Decl) {
  if (!_ghost && isGhostDecl(Decl)) return true;
  { clang::Decl::Kind declKind = Decl->getDeclContext()->getDeclKind();
    if ((declKind >= clang::Decl::firstFunction
          && declKind <= clang::Decl::lastFunction)
        && (static_cast<const clang::FunctionDecl*>(Decl->getDeclContext())
            ->getTemplatedKind() == clang::FunctionDecl::TK_FunctionTemplate))
      return true;
  }
  if (/* _parents. */ hasTemplateContext()) {
    if (_parents.isSemanticLocalClass())
      handleSemanticPostVisit(Decl->getDeclContext());
    else
      handlePostVisit(Decl->getLexicalDeclContext(), Decl->getDeclContext());
    registerTemplateDecl(Decl);
    return true;
  };
  handlePostVisit(Decl->getLexicalDeclContext(), Decl->getDeclContext());
  readGlobalComment(Decl, true /* mayHaveTemplate */);
  qualified_name enumName = _clangUtils->makeQualifiedName(*Decl);
  bool isFirstEnumName = true;
  bool isExternC = Clang_utils::isExternCContext(Decl);
  const char* name = strdup(enumName->decl_name);
  // if (!_parents.hasLexicalContext())
  //   name = copy_string(Decl->getQualifiedNameAsString());
  // else
  //   name = copy_string(Decl->getName().str());
  location loc = makeLocation(Decl->getSourceRange());
  list /* compilation_constant */ enums = NULL;
  ForwardReferenceList forwardEnums(enums);
  clang::EnumDecl::enumerator_iterator enumEnd = Decl->enumerator_end();
  for (clang::EnumDecl::enumerator_iterator enumIter = Decl->enumerator_begin();
      enumIter != enumEnd; ++enumIter) {
    qualified_name itemName = _clangUtils->makeQualifiedName(**enumIter);
    compilation_constant newEnum =
      compilation_constant_EnumCst(
        itemName,
        ekind_cons(
          isFirstEnumName ? enumName : qualified_name_dup(enumName),isExternC),
        (int64_t) enumIter->getInitVal().getLimitedValue(UINT64_MAX));
    isFirstEnumName = false;
    forwardEnums.insertContainer(newEnum);
  };
  if (isFirstEnumName)
    free_qualified_name(enumName);
  clang::QualType repr = Decl->getIntegerType();
  assert (repr->isIntegerType());
  ikind kind = _clangUtils->makeIntConstantType(repr.getTypePtr());
  clang::Decl::Kind declKind = Decl->getDeclContext()->getDeclKind();
  if (declKind == clang::Decl::TranslationUnit
      || declKind == clang::Decl::LinkageSpec
      /* !_parents.hasLexicalContext() */) {
    decl_or_impl_name decl_name = NULL;
    if (Decl->getDeclContext() == Decl->getLexicalDeclContext())
      decl_name = decl_or_impl_name_Declaration(name);
    else {
      qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
      free(const_cast<char*>(qual->decl_name));
      qual->decl_name = name;
      decl_name = decl_or_impl_name_Implementation(qual);
    };
    translation_unit_decl decl =
      translation_unit_decl_EnumDecl(
        loc, decl_name, kind, opt_some_container(enums),
        Clang_utils::isExternCContext(Decl),_ghost);
    _globals.insertContainer(decl);
  }
  else {
    if (Decl->getDeclContext()->isRecord() /* _parents.isClass()*/) {
      if (_parents.isClass()) {
        class_decl decl = class_decl_CEnum(loc, name, kind,
          opt_some_container(enums));
        _parents.add(decl);
      }
      else {
        qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
        free(const_cast<char*>(qual->decl_name));
        qual->decl_name = name;
        decl_or_impl_name decl_name = decl_or_impl_name_Implementation(qual);
        translation_unit_decl decl =
          translation_unit_decl_EnumDecl(
            loc, decl_name, kind, opt_some_container(enums), false,_ghost);
        if (_parents.hasLexicalContext()) _parents.add(decl);
        else _globals.insertContainer(decl);
      };
    }
    else {
      assert(Decl->getDeclContext()->isNamespace() /*_parents.isNamespace() */);
      decl_or_impl_name decl_name = NULL;
      if (Decl->getDeclContext() == Decl->getLexicalDeclContext())
        decl_name = decl_or_impl_name_Declaration(name);
      else {
        qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
        free(const_cast<char*>(qual->decl_name));
        qual->decl_name = name;
        decl_name = decl_or_impl_name_Implementation(qual);
      };
      translation_unit_decl decl =
        translation_unit_decl_EnumDecl(
          loc, decl_name, kind, opt_some_container(enums), false,_ghost);
      if (_parents.hasLexicalContext()) _parents.add(decl);
      else _globals.insertContainer(decl);
    };
  };
  // _parents.clearLocalPush();
  _tableForWaitingDeclarations.addDeclaration(Decl, _globals);
  // _instanceContexts.removeUnvisited(Decl);
  return true;
}

bool FramacVisitor::localLexicalVisitRecordDecl(clang::RecordDecl* Decl) {
  if (Decl->isThisDeclarationADefinition()) {
    _parents.pushLexicalLocalClass(Decl);
  }
  else {
  location loc = makeLocation(Decl->getLocation());
  std::cerr << "Unsupported declaration without definition in " << 
    Decl->getName().str() <<
    " at " << loc->filename1 << ":" << loc -> linenum1 << 
    "," << loc->charnum1 << 
    std::endl ; 
  assert(false);
  };
  return true;
}

bool FramacVisitor::localSemanticVisitRecordDecl(clang::RecordDecl* Decl) {
  const clang::CXXRecordDecl *RD = llvm::dyn_cast<clang::CXXRecordDecl>(Decl);
  const clang::ClassTemplateSpecializationDecl* TSD = NULL;
  if (Decl->getKind() == clang::Decl::ClassTemplateSpecialization)
    // do not handle partial specialization !
    TSD = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(Decl);
  if (RD || /* _parents. */ hasTemplateContext()) {
    if ((/* _parents. */hasTemplateContext()
        || (RD->getDescribedClassTemplate()
            && RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)
        || (RD->getKind() == clang::Decl::ClassTemplatePartialSpecialization))
        && (!RD || !TSD || !isTemplateInstance(
            RD->getTemplateSpecializationKind())
         /* RD->getTemplateSpecializationKind()
              < clang::TSK_ImplicitInstantiation */)) {
      registerTemplateDecl(Decl);
      assert((_state & 0x3) <= STempLexicalOut);
      if (RD->getDescribedClassTemplate()
          && (RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)) {
        // We are going to visit the content of template records
        _parents.pushSemanticLocalTemplateClass(Decl);
        _state = (State) (_state | STempLexicalOut | STemplateSynchronized);
      }
      else {
        _parents.pushSemanticLocalClass(Decl);
        _state = (State) (_state | STempLexicalOut);
      };
      _semanticContextForPostVisit = Decl;
      // returning false stops the global visit instead of preventing
      //    nested visit.
      return true;
    };
  };
  assert((_state & 0x3) <= STempLexicalOut);
  _parents.pushSemanticLocalClass(Decl);
  _state = (State) (_state | STempLexicalOut);
  _semanticContextForPostVisit = Decl;

  if (RD && RD->getKind() == clang::Decl::ClassTemplateSpecialization
      && TSD
      && isTemplateInstance(RD->getTemplateSpecializationKind())) {
    // do not handle partial specialization !
    // const clang::ClassTemplateSpecializationDecl* TSD
    //    = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(RD);
    _clangUtils->pushTemplateInstance(TSD->getSpecializedTemplate(),
        &TSD->getTemplateArgs());
  }
  return true;
}

/* inheritance */ list FramacVisitor::makeInheritanceList(
  clang::CXXRecordDecl* cxxDecl,
  std::vector<const clang::Decl*>* unvisitedInstanceDecls,
  std::vector<const clang::CXXRecordDecl*>* unvisitedBases) {
  /* inheritance */ list bodyInherits = NULL;
  ForwardReferenceList forwardInherits(bodyInherits);
  clang::CXXRecordDecl::base_class_iterator endBase = cxxDecl->bases_end();
  for (clang::CXXRecordDecl::base_class_iterator
      iterBase = cxxDecl->bases_begin(); iterBase != endBase; ++iterBase) {
    const clang::Type* type = iterBase->getType().getTypePtr();
    const clang::CXXRecordDecl* base = type->getAsCXXRecordDecl();
    if (!base) {
      std::cerr << "Unsupported Inheritance Type:"
          << iterBase->getType().getAsString ()
          << "\nAborting\n";
      exit(2);
    };
    int derivationPosition = 0;
    derivationPosition= _rttiTable.addDerivation(base, iterBase->isVirtual());
    if (unvisitedInstanceDecls)
      _tableForWaitingDeclarations.ensureGeneration(base,
                                                    *unvisitedInstanceDecls);
    else if (unvisitedBases && !_tableForWaitingDeclarations.hasVisited(base))
      unvisitedBases->push_back(base);
    clang::RecordDecl::TagKind tagKind = base->getTagKind();
    qualified_name baseName = _clangUtils->makeQualifiedName(*base);
    if (tagKind != clang::TTK_Class && tagKind != clang::TTK_Struct) {
      std::cerr << "Unsupported Inheritance Type:"
          << iterBase->getType().getAsString ()
          << "\nAborting\n";
      exit(2);
    };
    tkind templateParameters = _clangUtils->makeTemplateKind(base);
    vkind virt = iterBase->isVirtual() ? VVIRTUAL: VSTANDARD;
    access_kind akind = access_kind(iterBase->getAccessSpecifier());
    forwardInherits.insertContainer(
      inheritance_cons(
        baseName, templateParameters, akind, virt, derivationPosition));
  };
  return bodyInherits;
}

bool
FramaCIRGenAction::ImmediateGlobalDependency::globalInsertIfBooked(
    const clang::RecordDecl* decl,
    const std::list<LexicalLocalDeclContext>* context, 
    translation_unit_decl globalDecl,
    /* translation_unit_decl */ list*& insertionPlace,
    bool isInClass, const FramacVisitor& visitor) {
  NamespaceContext thisContext;
  if (context) {
    std::list<LexicalLocalDeclContext>::const_iterator
      iterEnd = context->end(), iter;
    for (iter = context->begin(); iter != iterEnd; ++iter) {
      if (!isInClass || &*iter != &context->back()) {
        assert(llvm::dyn_cast<clang::NamespaceDecl>(iter->getSynchro()));
        thisContext.push_back(std::make_pair(
            static_cast<const clang::NamespaceDecl*>(iter->getSynchro()),
            iter->getNamespacePlace()));
      };
    };
  };

  std::vector<PlaceForDeclGeneration>::iterator
    iterEnd = _placesForDecl.end(), iter;
  bool result = false;
  for (iter = _placesForDecl.begin(); !result && iter != iterEnd; ++iter)
    if (iter->getClassDecl() == decl) {
      iter->adjustPlace();
      int insertionStackSize = iter->getNamespaceContext().size(),
          stackSize = thisContext.size();
      /* translation_unit_decl */ list* place = iter->getPlace();
      translation_unit_decl toInsert = NULL;
      while (insertionStackSize > stackSize) {
        --insertionStackSize;
        assert(*place);
        /* translation_unit_decl */ list next = *place;
        const clang::NamespaceDecl* namespaceDecl = 
          iter->getNamespaceContext()[insertionStackSize].first;
        translation_unit_decl newNamespace = translation_unit_decl_Namespace(
          visitor.makeLocation(namespaceDecl->getSourceRange()),
          copy_string(namespaceDecl->getNameAsString()), next);
        *place = NULL;
        place = iter->getNamespaceContext()[insertionStackSize].second;
        next = *place;
        *place = cons_container(newNamespace, next);
      };
      while (insertionStackSize < stackSize) {
        --stackSize;
        const clang::NamespaceDecl*
          namespaceDecl = thisContext[stackSize].first;
        toInsert = translation_unit_decl_Namespace(
          visitor.makeLocation(namespaceDecl->getSourceRange()),
          copy_string(namespaceDecl->getNameAsString()),
          cons_container(toInsert, NULL));
      };
      while (insertionStackSize > 0
        && iter->getNamespaceContext()[insertionStackSize-1].first !=
          thisContext[insertionStackSize-1].first) { 
        --insertionStackSize;
        --stackSize;
        assert(*place);
        /* translation_unit_decl */ list next = *place;
        const clang::NamespaceDecl* namespaceDecl = 
          iter->getNamespaceContext()[insertionStackSize].first;
        translation_unit_decl newNamespace = translation_unit_decl_Namespace(
          visitor.makeLocation(namespaceDecl->getSourceRange()),
          copy_string(namespaceDecl->getNameAsString()), next);
        *place = NULL;
        place = iter->getNamespaceContext()[insertionStackSize].second;
        next = *place;
        *place = cons_container(newNamespace, next);

        namespaceDecl = thisContext[stackSize].first;
        toInsert = translation_unit_decl_Namespace(
          visitor.makeLocation(namespaceDecl->getSourceRange()),
          copy_string(namespaceDecl->getNameAsString()),
          cons_container(toInsert, NULL));
      };
      assert(*place);
      insertionPlace = place;
      if (toInsert) {
        /* translation_unit_decl */ list next = *place;
        *place = cons_container(toInsert, next);
        while (toInsert->cons_translation_unit_decl.Namespace.body) {
          /* translation_unit_decl */ list body
            = toInsert->cons_translation_unit_decl.Namespace.body;
          assert(body && !body->next && ((translation_unit_decl)
            body->element.container)->tag_translation_unit_decl == NAMESPACE);
          toInsert = (translation_unit_decl) body->element.container;
        };
        place = &toInsert->cons_translation_unit_decl.Namespace.body;
      };

      /* translation_unit_decl */ list next = *place;
      *place = cons_container(globalDecl, next);
      _placesForDecl.erase(iter);
      result = true;
    };
  return result;
}

bool
FramaCIRGenAction::ImmediateGlobalDependency::hasGlobalBooked(
    const clang::RecordDecl* decl) const {
  std::vector<PlaceForDeclGeneration>::const_iterator
    iterEnd = _placesForDecl.end(), iter;
  bool result = false;
  for (iter = _placesForDecl.begin(); !result && iter != iterEnd; ++iter)
    if (iter->getClassDecl() == decl)
      result = true;
  return result;
}

/* translation_unit_decl */ list*
FramaCIRGenAction::ImmediateGlobalDependency::adjustBeforePlace(
    const clang::RecordDecl* decl) {
  std::vector<PlaceForDeclGeneration>::iterator
    iterEnd = _placesForDecl.end(), iter;
  /* translation_unit_decl */ list* result = NULL;
  for (iter = _placesForDecl.begin(); !result && iter != iterEnd; ++iter)
    if (iter->getClassDecl() == decl) {
      iter->setNextPlace();
      result = iter->getPlace();
    };
  return result;
}

void
FramaCIRGenAction::ImmediateGlobalDependency::makeTemporaryDependentUnvisited(
    const clang::RecordDecl* baseClass, VisitTable& visitTable) {
  std::vector<PlaceForDeclGeneration>::iterator
    iterEnd = _placesForDecl.end(), iter;
  bool result = false;
  for (iter = _placesForDecl.begin(); !result && iter != iterEnd; ++iter)
    if (iter->getClassDecl() == baseClass) {
      visitTable.makeTemporaryUnvisited(iter->getOriginClassDecl());
      result = true;
    };
  assert(result);
}

void
FramaCIRGenAction::ImmediateGlobalDependency::makeTemporaryDependentVisited(
    const clang::RecordDecl* baseClass, VisitTable& visitTable,
    ForwardReferenceList& globals) {
  std::vector<PlaceForDeclGeneration>::iterator
    iterEnd = _placesForDecl.end(), iter;
  bool result = false;
  for (iter = _placesForDecl.begin(); !result && iter != iterEnd; ++iter)
    if (iter->getClassDecl() == baseClass) {
      visitTable.makeTemporaryVisited(iter->getOriginClassDecl(), globals);
      result = true;
    };
  assert(result);
}

bool FramacVisitor::VisitRecordDecl(clang::RecordDecl* Decl) {
  const clang::CXXRecordDecl *RD = llvm::dyn_cast<clang::CXXRecordDecl>(Decl);
  if (RD && RD->isLambda()) return true; // Lambda is handled directly by IR
  if (RD && (RD->getKind() == clang::Decl::ClassTemplatePartialSpecialization))
    return true; // case handled by VisitClassTemplatePartialSpecializationDecl
  if (!_ghost && isGhostDecl(Decl)) return true;
  if (/* _parents. */ hasTemplateContext() && _parents.isSemanticLocalClass())
    handleSemanticPostVisit(Decl->getParent());
  else
    handlePostVisit(Decl->getLexicalDeclContext(), Decl->getParent());
  if (!hasTemplateContext() || !_parents.isSemanticLocalClass())
    _lexicalContextForPostVisit = Decl;
  _semanticContextForPostVisit = Decl;
  if (RD && RD->getDescribedClassTemplate()
      && (RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)) {
    if (RD->getDescribedClassTemplate()->isThisDeclarationADefinition()) {
      pushTemplateContext();
      _state = (State) (_state | STemplateSynchronized);
    }
  }
  
  const clang::ClassTemplateSpecializationDecl* TSD = NULL;
  if (Decl->getKind() == clang::Decl::ClassTemplateSpecialization)
    // do not handle partial specialization !
    TSD = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(Decl);
  if (RD || /* _parents. */ hasTemplateContext()) {
    if ((/* _parents. */hasTemplateContext()
        || (RD->getDescribedClassTemplate()
            && RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)
        || (RD->getKind() == clang::Decl::ClassTemplatePartialSpecialization))
        && (!RD || !TSD || !isTemplateInstance(
            RD->getTemplateSpecializationKind())
         /* RD->getTemplateSpecializationKind()
              < clang::TSK_ImplicitInstantiation */)) {
      registerTemplateDecl(Decl);
      if (RD->getDescribedClassTemplate()
          && (RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)
          && RD->getDescribedClassTemplate()->isThisDeclarationADefinition()) {
        // We are going to visit the content of template records
        if (RD->isThisDeclarationADefinition()) {
          assert((_state & 0x3) <= STempLexicalOut);
          _parents.pushTemplateClass(Decl);
          _state = (State) (_state | STemplateSynchronized);
        };
      }
      else if (RD->isThisDeclarationADefinition()) {
        assert((_state & 0x3) <= STempLexicalOut);
        _parents.pushSemanticLocalClass(Decl);
        _state = (State) (_state | STempLexicalOut);
      }
      // returning false stops the global visit instead of preventing
      //    nested visit.
      return true;
    };
  };
  clang::Decl::Kind declKind = Decl->getDeclContext()->getDeclKind();
  if ((declKind >= clang::Decl::firstFunction
        && declKind <= clang::Decl::lastFunction)) {
    if (static_cast<const clang::FunctionDecl*>(Decl->getDeclContext())
            ->getTemplatedKind() == clang::FunctionDecl::TK_FunctionTemplate) {
      pushTemplateContext();
      assert((_state & 0x3) == SSynchronized);
      _parents.pushTemplateClass(Decl);
      _state = (State) (_state | STemplateSynchronized);
      return true;
    };
  };

  bool isDeportedInstantiation = false;
  // If a struct is used without having been declared (e.g. in a function prototype), then
  // clang inserts a fabricated declaration of the struct just prior to the function declaration.
  // Then if the function has an ACSL comment, the comment attempts to attach to the struct
  // declaration instead of the function prototype, causing a difficult to understand error message.
  // This test is also needed in postSemanticVisitRecordDecl.
  if (!Decl->isEmbeddedInDeclarator()) {
  readRecordComments(Decl, RD, TSD, isDeportedInstantiation);
  }

  tkind templateParameters = NULL;
  const char* name = _clangUtils->get_aggregate_name(Decl, &templateParameters);
  location loc = makeLocation(Decl->getSourceRange());
  ckind type = CCLASS;
  clang::RecordDecl::TagKind tagKind = Decl->getTagKind();
  switch (tagKind) {
    case clang::TTK_Struct:
      type = CSTRUCT;
      break;
    case clang::TTK_Class:
      type = CCLASS;
      break;
    case clang::TTK_Union:
      type = CUNION;
      break;
    default:
      std::cerr << "Unsupported Record Declaration:"
          << name
          << "\nAborting\n";
      exit(2);
  };
  option* body = NULL;
  /* inheritance list */ option inherits = opt_none();
  bool hasEnteredClass = false;
  std::vector<const clang::Decl*> unvisitedInheritance;
  std::vector<const clang::CXXRecordDecl*> unvisitedBases;
  bool isOwnInstance =
    (RD && RD->getKind() == clang::Decl::ClassTemplateSpecialization && TSD
      && isTemplateInstance(RD->getTemplateSpecializationKind()));
  bool isInstance = (isOwnInstance || !_instanceContexts.isEmpty());
  if (RD) {
    clang::CXXRecordDecl* cxxDecl = const_cast<clang::CXXRecordDecl*>(RD);
    if (Decl->isThisDeclarationADefinition() && cxxDecl->isCompleteDefinition())
    { _rttiTable.enterClass(cxxDecl);
      hasEnteredClass = true;
      /* inheritance */ list bodyInherits = makeInheritanceList(cxxDecl,
        isInstance ? &unvisitedInheritance : NULL,
        !isInstance ? &unvisitedBases : NULL);
      free(inherits);
      inherits = opt_some_container(bodyInherits);
    };
  };
  if (declKind == clang::Decl::TranslationUnit
      || declKind == clang::Decl::LinkageSpec
      /* !_parents.hasLexicalContext() */) {
    decl_or_impl_name decl_name = NULL;
    if (Decl->getDeclContext() == Decl->getLexicalDeclContext())
      decl_name = decl_or_impl_name_Declaration(name);
    else {
      qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
      free(const_cast<char*>(qual->decl_name));
      qual->decl_name = name;
      decl_name = decl_or_impl_name_Implementation(qual);
    }
    translation_unit_decl compound =
      translation_unit_decl_Compound(
        loc, decl_name, type, inherits, opt_none(), false,
        templateParameters, Clang_utils::isExternCContext(Decl), _ghost);
    body = &compound->cons_translation_unit_decl.Compound.body;
    if (hasEnteredClass)
      _rttiTable.setVirtualTag(compound->cons_translation_unit_decl.
        Compound.has_virtual);
    if (isOwnInstance) {
      // do not handle partial specialization !
      assert(_instanceContexts.isEmpty());
      pushInstanceContext(_tableForWaitingDeclarations.addInstanceClass(
            RD, compound));
      _state = (State) (_state | SInstanceSynchronized);
      unvisitedDecls().swap(unvisitedInheritance);
      _clangUtils->pushTemplateInstance(TSD->getSpecializedTemplate(),
          &TSD->getTemplateArgs());
      if (!_immediateGlobalDependency.isEmpty()
            && _immediateGlobalDependency.hasGlobalBooked(Decl)) {
        _immediateGlobalDependency.makeTemporaryDependentUnvisited(
          Decl, _tableForWaitingDeclarations);
        _globals.setBeforeBack(
          _immediateGlobalDependency.adjustBeforePlace(Decl));
      }
    }
    else {
      /* translation_unit_decl */ list* place = NULL;
      if (_immediateGlobalDependency.isEmpty()
          || !_immediateGlobalDependency.globalInsertIfBooked(Decl, NULL,
              compound, place, false, *this)) {
        if (hasEnteredClass
            || (Decl->getDeclContext() == Decl->getLexicalDeclContext()))
          _globals.insertContainer(compound);
        else
          _globals.insertBeforeContainer(compound);
        place = _globals.getBeforeBack();
      };
      if (!unvisitedBases.empty()) {
        assert(place);
        std::vector<const clang::CXXRecordDecl*>::const_reverse_iterator
          unvisitedIterEnd = unvisitedBases.rend(), unvisitedIter;
        for (unvisitedIter = unvisitedBases.rbegin();
            unvisitedIter != unvisitedIterEnd; ++unvisitedIter)
          _immediateGlobalDependency.addDeclarationPlace(*unvisitedIter,
              Decl, &_parents.getLexicalParent(), place);
      };
    };
  }
  else {
    if (Decl->getDeclContext()->isRecord() /* _parents.isClass() */) {
      assert(unvisitedBases.empty()
          && (_immediateGlobalDependency.isEmpty()
            || !_immediateGlobalDependency.hasGlobalBooked(Decl)));
      // get rid of a spurious nested declaration of the record
      if (Decl->isThisDeclarationADefinition()
          || Decl->getName() != ((const clang::RecordDecl*)
              Decl->getDeclContext())->getName()) {
        if (isOwnInstance) {
          // do not handle partial specialization !
          if (_instanceContexts.isEmpty()) {
            qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
            free(const_cast<char*>(qual->decl_name));
            qual->decl_name = name;
            decl_or_impl_name decl_name
                = decl_or_impl_name_Implementation(qual);
            translation_unit_decl compound =
              translation_unit_decl_Compound(
                loc, decl_name, type, inherits, opt_none(), false,
                templateParameters, false, _ghost);
            body = &compound->cons_translation_unit_decl.Compound.body;
            pushInstanceContext(_tableForWaitingDeclarations.addInstanceClass(
                RD, compound));
            _state = (State) (_state | SInstanceSynchronized);
            unvisitedDecls().swap(unvisitedInheritance);
            _clangUtils->pushTemplateInstance(TSD->getSpecializedTemplate(),
                &TSD->getTemplateArgs());
            if (hasEnteredClass)
              _rttiTable.setVirtualTag(compound->cons_translation_unit_decl.
                Compound.has_virtual);
          }
          else {
            assert(_parents.hasLexicalContext() && _parents.isClass());
            class_decl compound
              = class_decl_CCompound(loc, name, type, inherits, opt_none(),
                  templateParameters, false);
            class_decl compound_decl = class_decl_CCompound(copy_loc(loc),
              strdup(name), type, opt_none(), opt_none(),
              tkind_dup(templateParameters), false);
            _parents.add(compound_decl);
            body = &compound->cons_class_decl.CCompound.body;
            pushInstanceContext(_tableForWaitingDeclarations.addSubClass(
              _instanceContexts.firstClassContext(),
              _instanceContexts.lastSubClassContext(), RD, compound));
            _state = (State) (_state | SInstanceSynchronized);
            unvisitedDecls().swap(unvisitedInheritance);
            _clangUtils->pushTemplateInstance(TSD->getSpecializedTemplate(),
                &TSD->getTemplateArgs());
            if (hasEnteredClass)
              _rttiTable.setVirtualTag(compound->cons_class_decl.
                CCompound.has_virtual);
          };
        }
        else if (!_instanceContexts.isEmpty()) {
          assert(_parents.hasLexicalContext() && _parents.isClass());
          class_decl compound
            = class_decl_CCompound(loc, name, type, inherits, opt_none(),
                templateParameters, false);
          class_decl compound_decl = class_decl_CCompound(copy_loc(loc),
            strdup(name), type, opt_none(), opt_none(),
            tkind_dup(templateParameters), false);
          _parents.add(compound_decl);
          body = &compound->cons_class_decl.CCompound.body;
          pushInstanceContext(_tableForWaitingDeclarations.addSubClass(
            _instanceContexts.firstClassContext(),
            _instanceContexts.lastSubClassContext(), RD, compound));
          _state = (State) (_state | SInstanceSynchronized);
          unvisitedDecls().swap(unvisitedInheritance);
          if (TSD)
            _clangUtils->pushTemplateInstance(TSD->getSpecializedTemplate(),
                &TSD->getTemplateArgs());
          if (hasEnteredClass)
            _rttiTable.setVirtualTag(compound->cons_class_decl.
              CCompound.has_virtual);
        }
        else if (_parents.hasLexicalContext() && _parents.isClass()) {
          assert(unvisitedBases.empty());
          class_decl compound
            = class_decl_CCompound(loc, name, type, inherits, opt_none(),
                templateParameters, false);
          body = &compound->cons_class_decl.CCompound.body;
          _parents.add(compound);
          if (hasEnteredClass)
            _rttiTable.setVirtualTag(compound->cons_class_decl.
              CCompound.has_virtual);
        }
        else {
          qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
          free(const_cast<char*>(qual->decl_name));
          qual->decl_name = name;
          decl_or_impl_name decl_name = decl_or_impl_name_Implementation(qual);
          translation_unit_decl compound = translation_unit_decl_Compound(
             loc, decl_name, type, inherits, opt_none(), false,
             templateParameters, false, _ghost);
          body = &compound->cons_translation_unit_decl.Compound.body;
          if (hasEnteredClass)
            _rttiTable.setVirtualTag(compound->cons_translation_unit_decl.
              Compound.has_virtual);
          if (_parents.hasLexicalContext())
            _parents.add(compound);
          else
            _globals.insertContainer(compound);
        };
      }
      else {
        free(loc);
        if (inherits->is_some) {
          /* inheritance */ list bodyInherits
              = (list) inherits->content.container;
          while (bodyInherits) {
            list tmp = bodyInherits->next;
            free_inheritance((inheritance) bodyInherits->element.container);
            free(bodyInherits);
            bodyInherits = tmp;
          };
        }
        if (templateParameters)
          free_tkind(templateParameters);
        free(inherits);
        free(const_cast<char*>(name));
        return true;
      };
    }
    else if (declKind == clang::Decl::Function) {
      // ex: _GLIBCXX_RESOLVE_LIB_DEFECTS
      // 167.  Improper use of traits_type::length()
      assert(Decl->getDeclContext() == Decl->getLexicalDeclContext());
      qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
      free(const_cast<char*>(qual->decl_name));
      qual->decl_name = name;
      decl_or_impl_name decl_name = NULL;
      decl_name = decl_or_impl_name_Implementation(qual);
      translation_unit_decl compound =
        translation_unit_decl_Compound(
          loc, decl_name, type, inherits,
          opt_none(), false, templateParameters, false,_ghost);
      body = &compound->cons_translation_unit_decl.Compound.body;
      if (hasEnteredClass)
        _rttiTable.setVirtualTag(compound->cons_translation_unit_decl.
          Compound.has_virtual);
      _globals.insertContainer(compound);
    }
    else {
      assert(Decl->getDeclContext()->isNamespace() /*_parents.isNamespace() */);
      decl_or_impl_name decl_name = NULL;
      if (Decl->getDeclContext() == Decl->getLexicalDeclContext()
          && !(RD && RD->getKind() == clang::Decl::ClassTemplateSpecialization
            && TSD))
        decl_name = decl_or_impl_name_Declaration(name);
      else {
        qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
        free(const_cast<char*>(qual->decl_name));
        qual->decl_name = name;
        decl_name = decl_or_impl_name_Implementation(qual);
      };
      translation_unit_decl compound =
        translation_unit_decl_Compound(
          loc, decl_name, type, inherits,
          opt_none(), false, templateParameters, false,_ghost);
      body = &compound->cons_translation_unit_decl.Compound.body;
      if (hasEnteredClass)
        _rttiTable.setVirtualTag(compound->cons_translation_unit_decl.
          Compound.has_virtual);
      if (isOwnInstance) {
        // do not handle partial specialization !
        // const clang::ClassTemplateSpecializationDecl* TSD
        //    = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(RD);

        // assert(_instanceContexts.isEmpty());
        // could be technically inside an instance class since instantiation
        // points may be a friend class inside another class
        if (!_instanceContexts.isEmpty()) {
          // push _instanceContext to manage the instances of friend classes
          // inside our class.
          _alternateInstanceContexts.push_back(std::make_pair(Decl,
              FramaCIRGenAction::InstanceContexts()));
          _alternateInstanceContexts.back().second.swap(_instanceContexts);
        }
        pushInstanceContext(_tableForWaitingDeclarations.addInstanceClass(
              RD, compound));
        _state = (State) (_state | SInstanceSynchronized);
        unvisitedDecls().swap(unvisitedInheritance);
        _clangUtils->pushTemplateInstance(TSD->getSpecializedTemplate(),
            &TSD->getTemplateArgs());
        if (!_immediateGlobalDependency.isEmpty()
              && _immediateGlobalDependency.hasGlobalBooked(Decl))
          _immediateGlobalDependency.makeTemporaryDependentUnvisited(
            Decl, _tableForWaitingDeclarations);
      }
      else {
        /* translation_unit_decl */ list* place = NULL;
        if (_immediateGlobalDependency.isEmpty()
            || !_immediateGlobalDependency.globalInsertIfBooked(Decl,
                &_parents.getLexicalParent(), compound, place, false, *this)) {
          if (_parents.hasLexicalContext()) {
            // work around desynchronization between clang and our own
            // context. Apparently only manifests for template instances
            // that do not declare anything (hence are useless): just drop
            // them.
            // TODO: get rid of our own LocalContext and rely solely on
            // Clang's.
            if (_parents.isNamespace()) _parents.add(compound, &place);
          else {
            _globals.insertContainer(compound);
            place = _globals.getBeforeBack();
          };
        };
        };
        if (!unvisitedBases.empty()) {
          assert(place);
          std::vector<const clang::CXXRecordDecl*>::const_reverse_iterator
            unvisitedIterEnd = unvisitedBases.rend(), unvisitedIter;
          for (unvisitedIter = unvisitedBases.rbegin();
              unvisitedIter != unvisitedIterEnd; ++unvisitedIter)
            _immediateGlobalDependency.addDeclarationPlace(*unvisitedIter,
                Decl, &_parents.getLexicalParent(), place);
        };
      };
    };
  };

  assert(_state == SSynchronized || _state == SInstanceSynchronized);
  _parents.pushClass(*body, Decl);
  return true;
}

bool FramacVisitor::VisitClassTemplatePartialSpecializationDecl(
  clang::CXXRecordDecl* Decl) {
  if (!_ghost && isGhostDecl(Decl)) return true;
  if (/* _parents. */ hasTemplateContext() && _parents.isSemanticLocalClass())
    handleSemanticPostVisit(Decl->getParent());
  else
    handlePostVisit(Decl->getLexicalDeclContext(), Decl->getParent());
  if (!hasTemplateContext() || !_parents.isSemanticLocalClass())
    _lexicalContextForPostVisit = Decl;
  _semanticContextForPostVisit = Decl;
  if (Decl->isThisDeclarationADefinition())
    pushTemplateContext();

  registerAttachedNextTemplateDecl(Decl);
  if (Decl->isThisDeclarationADefinition()) {
    // We are going to visit the content of template records
    if (/* _parents. */ hasTemplateContext() && _parents.isSemanticLocalClass())
    { assert((_state & 0x3) == STempLexicalOut);
      _parents.pushSemanticLocalTemplateClass(Decl);
    }
    else {
      assert((_state & 0x3) == SSynchronized);
      _parents.pushTemplateClass(Decl);
    };
    _state = (State) (_state | STemplateSynchronized);
  };

  // returning false stops the global visit instead of preventing
  //    nested visit.
  return true;
}

void FramacVisitor::postLexicalVisitRecordDecl(clang::RecordDecl* Decl,
    int& templateContextsNumber) {
  const clang::CXXRecordDecl *RD = llvm::dyn_cast<clang::CXXRecordDecl>(Decl);
  const clang::ClassTemplateSpecializationDecl* TSD = NULL;
  if (Decl->getKind() == clang::Decl::ClassTemplateSpecialization)
    TSD = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(Decl);
  if ((templateContextsNumber > 0
        || (RD && RD->getDescribedClassTemplate()
          && RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)
        || ( RD->getKind() == clang::Decl::ClassTemplatePartialSpecialization))
      && (!RD || !TSD || !isTemplateInstance(
          RD->getTemplateSpecializationKind()))) {
    if (Decl->isThisDeclarationADefinition()) {
      const clang::DeclContext* parentDecl = Decl->getLexicalDeclContext();
      clang::Decl::Kind declKind = parentDecl->getDeclKind();
      if ((declKind >= clang::Decl::firstFunction
            && declKind <= clang::Decl::lastFunction))
         parentDecl = parentDecl->getLexicalParent();
      // the case STemp.. occurs because postSemanticVisitRecordDecl
      //   has not still been called.
      // assert(_state == STemplateSynchronized);
      assert(templateContextsNumber > 0);
      if (RD &&
          ((RD->getDescribedClassTemplate()
            && (RD->getDescribedClassTemplate()->getTemplatedDecl() == RD))
          || (RD->getKind()==clang::Decl::ClassTemplatePartialSpecialization)))
        --templateContextsNumber;
      _parents.lexicalSynchronizeWith(parentDecl, *this);
      if (templateContextsNumber == 0)
         _state = hasInstanceContext() ? SInstanceSynchronized : SSynchronized;
      else
         _state = STemplateSynchronized;
    };
    return;
  };

  bool hasInsertedGlobals = false;
  if (RD && RD->getKind() == clang::Decl::ClassTemplateSpecialization && TSD
      && isTemplateInstance(RD->getTemplateSpecializationKind())) {
    if (_parents.getLexicalParent().back().isLocalClass())
      goto LSynchronization;
    // do not handle partial specialization!
    bool hasAlternate = false;
    if (_instanceContexts.size() == 1) {
      VisitTable::MissingClassGeneration* lastInstance
          = &_instanceContexts.lastClassContext();
      assert(lastInstance->isInstanceClass());
      translation_unit_decl* last = _globals.getBack()
        ? (translation_unit_decl*) &_globals.getBack()->element.container
        : NULL;
      list* beforeBack = _globals.getBack()
        ? &_globals.getBack()->next : &_globals.getFront();
      list oldBack = _globals.getBack();
      translation_unit_decl originalCompound = ((VisitTable
        ::InstanceClassGeneration*) lastInstance)->getWaitingClassDeclaration();
      _tableForWaitingDeclarations.setInstanceClassAsComplete(
          (VisitTable::InstanceClassGeneration*) lastInstance, _globals);
      if (_globals.getBack() && last !=
          (translation_unit_decl*) &_globals.getBack()->element.container) {
        if (!_immediateGlobalDependency.isEmpty()
            && _immediateGlobalDependency.hasGlobalBooked(Decl)) {
          assert(*beforeBack);
          translation_unit_decl compound
            = (translation_unit_decl) (*beforeBack)->element.container;
          list compoundPlace = *beforeBack;
          *beforeBack = (*beforeBack)->next; // temporary remove compound
          // remove compound original with a consistent _globals
          if (_globals.getBack() == compoundPlace)
            _globals.resetBack(oldBack ? oldBack : _globals.getFront());
          free(compoundPlace);
          _immediateGlobalDependency.makeTemporaryDependentVisited(
              Decl, _tableForWaitingDeclarations, _globals);
          /* translation_unit_decl */ list* place = NULL;
          _immediateGlobalDependency.globalInsertIfBooked(Decl,
              &_parents.getLexicalParent(), compound, place, true, *this);
          _globals.setBeforeBack(place);
        }
        else
          _globals.setBeforeBack(beforeBack);
        hasInsertedGlobals = true;
      }
      else { // insert at least the declaration
        assert(originalCompound->tag_translation_unit_decl == COMPOUND);
        _globals.insertContainer(translation_unit_decl_Compound(
          copy_loc(originalCompound->cons_translation_unit_decl.Compound.loc),
          decl_or_impl_name_dup(originalCompound->cons_translation_unit_decl
            .Compound.name),
          originalCompound->cons_translation_unit_decl.Compound.kind,
          opt_none() /* inherits */, opt_none() /* body */,
          false /* has_virtual */,
          tkind_dup(originalCompound->cons_translation_unit_decl
            .Compound.template_kind), false /* is_extern_c_context */,
          false /* is_ghost */));
        _globals.setBeforeBack(beforeBack);
        hasInsertedGlobals = true;
      }
      // are we in an instance of a friend class inside another class?
      hasAlternate = !_alternateInstanceContexts.empty()
          && _alternateInstanceContexts.back().first == Decl;
      assert(!hasAlternate
          || !_alternateInstanceContexts.back().second.isEmpty());
    }
    else {
      /* class_decl */ list* parentBody = NULL;
      VisitTable::MissingSubClassGeneration* lastInstance
          = _instanceContexts.lastSubClassContext(&parentBody);
      assert(lastInstance && parentBody);
      class_decl originalCompound = lastInstance->getWaitingSubClassDecl();
      ForwardReferenceList* body = _parents.getDeclList(parentBody);
      ForwardReferenceList additionalBody;
      if (!body)
        additionalBody = ForwardReferenceList(*parentBody);
      if (!lastInstance->setAsComplete(body ? *body : additionalBody)) {
        assert(originalCompound->tag_class_decl == CCOMPOUND);
        (body ? *body : additionalBody).insertContainer(class_decl_CCompound(
          copy_loc(originalCompound->cons_class_decl.CCompound.loc),
          strdup(originalCompound->cons_class_decl.CCompound.name),
          originalCompound->cons_class_decl.CCompound.kind,
          opt_none() /* inherits */, opt_none() /* body */,
          tkind_dup(originalCompound->cons_class_decl.CCompound.template_kind),
          false /* has_virtual */));
      };
      _tableForWaitingDeclarations.addSubDeclaration(Decl, 
          _instanceContexts.getRootDecl(),
          lastInstance /* should be the stack of subclasses */,
          _globals);
    };
    assert(_state >= SInstanceSynchronized);
    popInstanceContext(RD);
    if (hasAlternate) {
      // we are in an instance of a friend class inside another class.
      // so we restore the initial class context.
      _instanceContexts.swap(_alternateInstanceContexts.back().second);
      _alternateInstanceContexts.pop_back();
    };
    if (!hasInstanceContext())
      _state = (State) (_state & 0x3);
    if (_templateCommentIndex >= -1)
      _templateCommentIndex = -2;
  }
  else {
    bool hasVisited = false;
    if (Decl->getDeclContext()->isRecord() /* _parents.isClass() */) {
      // get rid of a spurious nested declaration of the record
      if (Decl->isThisDeclarationADefinition()
          || Decl->getName() != ((const clang::RecordDecl*)
              Decl->getDeclContext()/* _parents.getSynchro() */)->getName()) {
        if (_instanceContexts.size() > 1) {
          /* class_decl */ list* parentBody = NULL;
          VisitTable::MissingSubClassGeneration* lastInstance
              = _instanceContexts.lastSubClassContext(&parentBody);
          assert(lastInstance && parentBody);
          class_decl originalCompound = lastInstance->getWaitingSubClassDecl();
          ForwardReferenceList* body = _parents.getDeclList(parentBody);
          ForwardReferenceList additionalBody;
          if (!body)
            additionalBody = ForwardReferenceList(*parentBody);
          if (!lastInstance->setAsComplete(body ? *body : additionalBody)) {
            assert(originalCompound->tag_class_decl == CCOMPOUND);
            (body ? *body : additionalBody).insertContainer(class_decl_CCompound(
              copy_loc(originalCompound->cons_class_decl.CCompound.loc),
              strdup(originalCompound->cons_class_decl.CCompound.name),
              originalCompound->cons_class_decl.CCompound.kind,
              opt_none() /* inherits */, opt_none() /* body */,
              tkind_dup(originalCompound->cons_class_decl.CCompound.template_kind),
              false /* has_virtual */));
          };
          assert(_state >= SInstanceSynchronized);
          popInstanceContext(RD);
          if (!hasInstanceContext())
            _state = (State) (_state & 0x3);
          _tableForWaitingDeclarations.addSubDeclaration(Decl, 
              _instanceContexts.getRootDecl(),
              lastInstance /* should be the stack of subclasses */,
              _globals);
          hasVisited = true;
        };
      };
    };
    if (!hasVisited)
      _tableForWaitingDeclarations.addDeclaration(Decl, _globals);
    // _instanceContexts.removeUnvisited(Decl);
  };

  if (!_parents.hasLexicalLocalContext(Decl)) {
    if (llvm::isa<clang::CXXRecordDecl>(Decl)) {
      clang::CXXRecordDecl* cxxDecl = static_cast<clang::CXXRecordDecl*>(Decl);
      if (cxxDecl->isCompleteDefinition()) {
        location loc = makeLocation(Decl->getSourceRange());
        assert(_parents.isClass());
        _rttiTable.exitClass(*_clangUtils, _parents.getClassContent(), _globals,
          loc);
        free_location(loc);
      };
    };
  }
  else { /* assert(false); */ }

LSynchronization:
  const clang::DeclContext* parentDecl = Decl->getLexicalDeclContext();
  clang::Decl::Kind declKind = parentDecl->getDeclKind();
  if ((declKind >= clang::Decl::firstFunction
        && declKind <= clang::Decl::lastFunction))
    parentDecl = parentDecl->getLexicalParent();
  _parents.lexicalSynchronizeWith(parentDecl, *this);
  if (!hasTemplateContext())
    _state = hasInstanceContext() ? SInstanceSynchronized : SSynchronized;
  else
    _state = STemplateSynchronized;

  if (hasInsertedGlobals && _parents.isNamespace())
    _parents.reopenLexicalHierarchy(*this);
}

void FramacVisitor::postSemanticVisitRecordDecl(clang::RecordDecl* Decl) {
  const clang::CXXRecordDecl *RD = llvm::dyn_cast<clang::CXXRecordDecl>(Decl);
  const clang::ClassTemplateSpecializationDecl* TSD = NULL;
  if (Decl->getKind() == clang::Decl::ClassTemplateSpecialization)
    // do not handle partial specialization !
    TSD = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(Decl);
  if ((/* _parents. */ hasTemplateContext()
        || (RD && RD->getDescribedClassTemplate()
          && RD->getDescribedClassTemplate()->getTemplatedDecl() == RD)
        || ( RD->getKind() == clang::Decl::ClassTemplatePartialSpecialization))
      && (!RD || !TSD || !isTemplateInstance(
          RD->getTemplateSpecializationKind()))) {
    registerTemplateDecl(Decl);
    const clang::DeclContext* parentDecl = Decl->getParent();
    clang::Decl::Kind declKind = parentDecl->getDeclKind();
    if ((declKind >= clang::Decl::firstFunction
          && declKind <= clang::Decl::lastFunction))
      parentDecl = parentDecl->getParent();
    if (RD->isThisDeclarationADefinition()) {
      // note that the semantic parent Stack may be greater than one.
      assert(_state==STemplateSynchronized||_state==STemplateTempLexicalOut);
      _parents.semanticSynchronizeWith(parentDecl, *this);
      if (!hasTemplateContext()) {
        if (_state == STemplateSynchronized) {
          assert(!_parents.isSemanticLocalClass());
          _state = hasInstanceContext() ? SInstanceSynchronized : SSynchronized;
        }
        else if (_parents.isSemanticLocalClass())
          _state = hasInstanceContext() ? SInstanceTempLexicalOut
             : STempLexicalOut;
        else
          _state = hasInstanceContext() ? SInstanceSynchronized : SSynchronized;
      }
    }
    else {
      // assert(_parents.getSemanticParentContext() == parentDecl);
      if (_parents.getSemanticParentContext() != parentDecl) {
        _parents.semanticSynchronizeWith(parentDecl, *this);
        if (!hasTemplateContext()) {
          if (_state == STemplateSynchronized) {
            assert(!_parents.isSemanticLocalClass());
            _state=hasInstanceContext() ? SInstanceSynchronized : SSynchronized;
          }
          else if (_parents.isSemanticLocalClass())
            _state = hasInstanceContext() ? SInstanceTempLexicalOut
               : STempLexicalOut;
          else
            _state=hasInstanceContext() ? SInstanceSynchronized : SSynchronized;
        }
        if (llvm::dyn_cast<const clang::NamespaceDecl>(parentDecl)) {
        const clang::NamespaceDecl* fst = llvm::dyn_cast
            <const clang::NamespaceDecl>(_parents.getSemanticParentContext()),
          *snd = llvm::dyn_cast<const clang::NamespaceDecl>(parentDecl);
        assert(fst && snd && fst->getName() == snd->getName());
      };
    };
    };
    if (_templateCommentIndex >= -1)
      _templateCommentIndex = -2;
    return;
  };

  // See the comment in VisitRecordDecl about the isEmbeddedInDeclarator check
  if (!_parents.hasSemanticLocalContext(Decl)&& !Decl->isEmbeddedInDeclarator()) {
    readInnerRecordComments(Decl);
  }

  bool isOwnInstance = RD
    && RD->getKind() == clang::Decl::ClassTemplateSpecialization && TSD
    && isTemplateInstance(RD->getTemplateSpecializationKind());
  // should be done by postLexicalVisitRecordDecl.
  if (isOwnInstance) {
    // do not handle partial specialization!
    _clangUtils->popTemplateInstance(TSD->getSpecializedTemplate());
    if (_templateCommentIndex >= -1)
      _templateCommentIndex = -2;
  }
  else if (Decl->getDeclContext()->isRecord() /* _parents.isClass() */
      && (Decl->isThisDeclarationADefinition()
          || Decl->getName() != ((const clang::RecordDecl*)
              Decl->getDeclContext())->getName())
      && !_instanceContexts.isEmpty() && TSD)
    // this condition is a special case of VisitRecordDecl
    //   to get the symmetric case of _clangUtils->pushTemplateInstance
    _clangUtils->popTemplateInstance(TSD->getSpecializedTemplate());

  const clang::DeclContext* parentDecl = Decl->getParent();
  clang::Decl::Kind declKind = parentDecl->getDeclKind();
  if ((declKind >= clang::Decl::firstFunction
        && declKind <= clang::Decl::lastFunction))
    parentDecl = parentDecl->getParent();
  _parents.semanticSynchronizeWith(parentDecl, *this);
  if (!hasTemplateContext()) {
    if (_state == STemplateSynchronized) {
      assert(!_parents.isSemanticLocalClass());
      _state = hasInstanceContext() ? SInstanceSynchronized : SSynchronized;
    }
    else if (_parents.isSemanticLocalClass())
      _state = hasInstanceContext() ? SInstanceTempLexicalOut
         : STempLexicalOut;
    else
      _state = hasInstanceContext() ? SInstanceSynchronized : SSynchronized;
  }
  else {
    if (_state == STemplateSynchronized)
      assert(!_parents.isSemanticLocalClass());
    else if (_parents.isSemanticLocalClass())
      _state = STemplateTempLexicalOut;
    else
      _state = STemplateSynchronized;
  }
}

bool FramacVisitor::VisitTypedefNameDecl(clang::TypedefNameDecl* Decl) {
  if (!_ghost && isGhostDecl(Decl)) return true;
  { clang::Decl::Kind declKind = Decl->getDeclContext()->getDeclKind();
    if (declKind >= clang::Decl::firstFunction
          && declKind <= clang::Decl::lastFunction) {
      if (static_cast<const clang::FunctionDecl*>(Decl->getDeclContext())
          ->getTemplatedKind() != clang::FunctionDecl::TK_FunctionTemplate)
        _tableForWaitingDeclarations.addDeclaration(Decl, _globals);
      return true;
    };
  }
  if (/* _parents. */ hasTemplateContext() && _parents.isSemanticLocalClass())
    handleSemanticPostVisit(Decl->getDeclContext());
  else
    handlePostVisit(Decl->getLexicalDeclContext(), Decl->getDeclContext());
  if (/* _parents. */ hasTemplateContext()
      // type alias is basically a templated typedef
      || Decl->getKind() == clang::Decl::TypeAlias) {
    registerTemplateDecl(Decl);
    // because sometimes instance declarations depends on template typedef.
    _tableForWaitingDeclarations.addDeclaration(Decl, _globals);
    return true;
  };
  if (Decl->getSourceRange().getBegin().isValid())
    readGlobalComment(Decl, true /* mayHaveTemplate */);

  if (_generatedTypedefsInAdvance.find(Decl)
      == _generatedTypedefsInAdvance.end()) {
    const char* name;
    if (!_parents.hasLexicalContext())
      name = copy_string(Decl->getQualifiedNameAsString());
    else
      name = copy_string(Decl->getName().str());
    location loc = makeLocation(Decl->getSourceRange());
    qual_type targetType=
      makeDefaultNameType(Decl->getLocation(), Decl->getUnderlyingType());
    if (!_parents.hasLexicalContext()) {
      translation_unit_decl decl =
        translation_unit_decl_Typedef(
          loc, decl_or_impl_name_Declaration(name), targetType,
          Clang_utils::isExternCContext(Decl->getDeclContext()),_ghost);
      _globals.insertContainer(decl);
    }
    else {
      if (_parents.isClass()) {
        class_decl decl = class_decl_CTypedef(loc, name, targetType);
        _parents.add(decl);
      }
      else {
        assert(_parents.isNamespace());
        translation_unit_decl decl =
          translation_unit_decl_Typedef(
            loc, decl_or_impl_name_Declaration(name), targetType,
            Clang_utils::isExternCContext(Decl->getDeclContext()), _ghost);
        _parents.add(decl);
      };
    };
  };
  _tableForWaitingDeclarations.addDeclaration(Decl, _globals);
  return true;
}

bool FramacVisitor::VisitVarDecl(clang::VarDecl* Decl) {
  if (!_ghost && isGhostDecl(Decl)) return true;
  { clang::Decl::Kind declKind = Decl->getDeclContext()->getDeclKind();
    if ((declKind >= clang::Decl::firstFunction
          && declKind <= clang::Decl::lastFunction)
        && (static_cast<const clang::FunctionDecl*>(Decl->getDeclContext())
            ->getTemplatedKind() == clang::FunctionDecl::TK_FunctionTemplate))
      return true;
  }
  // static local variables get visited as well
  if (Decl->getDeclContext()->isFunctionOrMethod()) return true;
  if (Decl->hasGlobalStorage()) {
    if (/* _parents. */ hasTemplateContext() && _parents.isSemanticLocalClass())
      handleSemanticPostVisit(Decl->getDeclContext());
    else
      handlePostVisit(Decl->getLexicalDeclContext(), Decl->getDeclContext());
  };
  if (/* _parents. */ hasTemplateContext()) {
    registerTemplateDecl(Decl);
    return true;
  };

  if (!Decl->hasGlobalStorage())
    return true;
  readGlobalComment(Decl, true /* mayHaveTemplate */);

  const char* name = _clangUtils->get_field_name(Decl);
  location loc = makeLocation(Decl->getSourceRange());
  clang::QualType type = Decl->getType();
  ickind constantType = ICLITERAL;
  int64_t value = 0;
  if (type.isConstQualified()
      && (type->getTypeClass() == clang::Type::Builtin)) {
    auto builtinType =
      llvm::dyn_cast<const clang::BuiltinType>(type.getTypePtr());
    assert(builtinType);
    if (builtinType->isInteger()) {
      clang::EvaluatedStmt *Eval = Decl->ensureEvaluatedStmt();
      if (Eval->WasEvaluated || Eval->Value) {
        clang::APValue* resultingValue = Decl->evaluateValue();
        if (resultingValue) {
          constantType = Decl->hasExternalStorage()?ICEXTERNCONST:ICSTATICCONST;
          value = resultingValue->getInt().getExtValue();
        }
      };
    };
  };
  qual_type declarationType = makeDefaultType(Decl->getLocation(), type);
  if (Decl->getCanonicalDecl()->getStorageClass() == clang::SC_Static &&
      !Decl->isOutOfLine())
    declarationType->qualifier = cons_plain(STATIC, declarationType->qualifier);
  option /*init_expr*/ init = opt_none();
  if (Decl->getInit()) {
    variable v = variable_Global(_clangUtils->makeQualifiedName(*Decl));
    const clang::Type* typ = Decl->getType().getTypePtr();
    if (isRecordOrRecordRef(typ) || typ->isConstantArrayType()) {
      location loc = makeLocation(Decl->getSourceRange());
      _implicitThisStar = expression_cons(loc,exp_node_Variable(v));
    }
    free(init);
    init = opt_some_container(makeInitExpr(Decl->getType(), Decl->getInit()));
    if (!_cleanups.empty()) {
      std::cerr << "Unsupported statements to clean the expression\n";
#if CLANG_VERSION_MAJOR >= 11
      Decl->getInit()->dump(llvm::errs(), *_context);
#else
      Decl->getInit()->dump(llvm::errs(), _context->getSourceManager());
#endif
      std::cerr << "\ncontinue the translation\n";
      _cleanups.clear();
    };
  };

  clang::Decl::Kind declKind = Decl->getDeclContext()->getDeclKind();
  if (declKind == clang::Decl::TranslationUnit
      || declKind == clang::Decl::LinkageSpec
      /* !_parents.hasLexicalContext() */) {
    translation_unit_decl decl = NULL;
    if (constantType == ICLITERAL) {
      decl_or_impl_name decl_name = NULL;
      if (Decl->getDeclContext() == Decl->getLexicalDeclContext())
        decl_name = decl_or_impl_name_Declaration(name);
      else {
        qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
        free(const_cast<char*>(qual->decl_name));
        qual->decl_name = name;
        decl_name = decl_or_impl_name_Implementation(qual);
      };
      decl =
        translation_unit_decl_GlobalVarDecl(
          loc, decl_name, declarationType, init,
          Decl->isInExternCContext(), _ghost);
    }
    else {
      ikind kind = _clangUtils->makeIntConstantType(type.getTypePtr());
      // Apparently isExternC() is not strong enough for this kind of 
      // identifiers
      decl =
        translation_unit_decl_StaticConst(
          loc, name, kind, constantType, value,
          Decl->isInExternCContext(), _ghost);
    };
    _globals.insertContainer(decl);
  }
  else {
    if (Decl->getDeclContext()->isRecord() /* _parents.isClass() */) {
      if (constantType == ICLITERAL) {
        if (_parents.hasLexicalContext() && _parents.isClass()) {
          class_decl decl =
            class_decl_CFieldDecl(
              loc, name, declarationType, opt_none(), false);
          _parents.add(decl);
        }
        else {
          qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
          free(const_cast<char*>(qual->decl_name));
          qual->decl_name = name;
          decl_or_impl_name decl_name = decl_or_impl_name_Implementation(qual);
          translation_unit_decl decl =
            translation_unit_decl_GlobalVarDecl(
              loc, decl_name, declarationType, init, false, _ghost);
          if (_parents.hasLexicalContext()) {
            _parents.add(decl);
          } else {
            _globals.insertContainer(decl);
          };
        };
      }
      else {
        assert(_parents.isClass());
        ikind kind = _clangUtils->makeIntConstantType(type.getTypePtr());
        class_decl decl = class_decl_CStaticConst(loc, name, kind,
            constantType, value);
        _parents.add(decl);
      };
    }
    else {
      translation_unit_decl decl = NULL;
      if (constantType== ICLITERAL) {
        decl_or_impl_name decl_name = NULL;
        if (!Decl->getDeclContext()->isNamespace()
            || (Decl->getDeclContext() == Decl->getLexicalDeclContext()))
          decl_name = decl_or_impl_name_Declaration(name);
        else {
          qualified_name qual = _clangUtils->makeQualifiedName(*Decl);
          free(const_cast<char*>(qual->decl_name));
          qual->decl_name = name;
          decl_name = decl_or_impl_name_Implementation(qual);
        }
        decl =
          translation_unit_decl_GlobalVarDecl(
            loc, decl_name, declarationType, init, false, _ghost);
      }
      else {
        ikind kind = _clangUtils->makeIntConstantType(type.getTypePtr());
        decl =
          translation_unit_decl_StaticConst(
            loc, name, kind, constantType, value, false, _ghost);
      };
      if (_parents.hasLexicalContext())
        _parents.add(decl);
      else {
        _globals.insertContainer(decl);
      };    
    };
  };
  _tableForWaitingDeclarations.addDeclaration(Decl, _globals);
  _instanceContexts.removeUnvisited(Decl);
  return true;
}

bool FramacVisitor::VisitFieldDecl(clang::FieldDecl* Decl) {
  if (!_ghost && isGhostDecl(Decl)) return true;
  if (/* _parents. */ hasTemplateContext()) {
    if (_parents.isSemanticLocalClass())
      handleSemanticPostVisit(Decl->getDeclContext());
    else
      handlePostVisit(Decl->getLexicalDeclContext(), Decl->getDeclContext());
    registerTemplateDecl(Decl);
    return true;
  };
  handlePostVisit(Decl->getLexicalDeclContext(), Decl->getDeclContext());
  assert(_parents.hasLexicalContext() && _parents.isClass());
  readGlobalComment(Decl, true /* mayHaveTemplate */);

  const char* name = _clangUtils->get_field_name(Decl);
  clang::SourceLocation sloc = Decl->getLocation(); 
  location loc = makeLocation(Decl->getSourceRange());
  /* Decl may be a bitfield */
  clang::QualType type = Decl->getType();
  qual_type declarationType;
  if (!Clang_utils::isExternCContext(Decl->getDeclContext()))
    declarationType = makeDefaultType(sloc, type);
  else
    declarationType = _clangUtils->makePODType(sloc, type);

  class_decl decl;
  if (!Decl->isBitField())
    decl =
      class_decl_CFieldDecl(
        loc, name, declarationType, opt_none(), Decl->isMutable());
  else {
    int size = Decl->getBitWidthValue(*_context);
    decl =
      class_decl_CFieldDecl(
        loc, name, declarationType, opt_some_plain(size), Decl->isMutable());
  };
  _parents.add(decl);
  _tableForWaitingDeclarations.addDeclaration(Decl, _globals);
  _instanceContexts.removeUnvisited(Decl);
  return true;
}

void FramacVisitor::postHandleTranslationUnit() {
  _parents.clear(*this);
  int previousComment = _commentIndex;
  _commentIndex = (int) _annotationCommentList.size()-2;
  while (previousComment <= _commentIndex) {
    ++previousComment;
    switch (_annotationCommentList[previousComment]->getKind()) {
      case AnnotationComment::KGlobal:
        parseGlobalComment(*_annotationCommentList[previousComment],
            _context->getTranslationUnitDecl());
        break;
      case AnnotationComment::KGhost:
          parseGhostGlobal(*_annotationCommentList[previousComment],
                           _context->getTranslationUnitDecl());
          break;
      default:
        parseDefaultComment(*_annotationCommentList[previousComment]);
        break;
    };
  };
}