/******************************************************************************
 * Copyright (c) 2000-2021 Ericsson Telecom AB
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
 *
 * Contributors:
 *   Baji, Laszlo
 *   Balasko, Jeno
 *   Baranyi, Botond
 *   Beres, Szabolcs
 *   Delic, Adam
 *   Feher, Csaba
 *   Forstner, Matyas
 *   Kovacs, Ferenc
 *   Raduly, Csaba
 *   Szabados, Kristof
 *   Szabo, Bence Janos
 *   Szabo, Janos Zoltan – initial implementation
 *   Zalanyi, Balazs Andor
 *
 ******************************************************************************/
#include "../../common/dbgnew.hh"
#include "Statement.hh"
#include "Ttcnstuff.hh"
#include "../TypeCompat.hh"
#include "../CompType.hh"
#include "../CompField.hh"
#include "../SigParam.hh"
#include "TtcnTemplate.hh"
#include "ILT.hh"
#include "ArrayDimensions.hh"
#include "../Int.hh"
#include "../main.hh"
#include "Attributes.hh"
#include "compiler2/Valuestuff.hh"

namespace Ttcn {

  // =================================
  // ===== StatementBlock
  // =================================

  StatementBlock::StatementBlock()
    : Scope(), checked(false), labels_checked(false), my_sb(0), my_def(0), exception_handling(EH_NONE),
    finally_block(NULL), dynamic_template(NULL)
  {
  }

  StatementBlock::~StatementBlock()
  {
    for(size_t i=0; i<stmts.size(); i++)
      delete stmts[i];
    stmts.clear();
    defs.clear();
    labels.clear();
    for (size_t i = 0; i < catch_blocks.size(); ++i) {
      delete catch_blocks[i];
    }
    catch_blocks.clear();
    delete finally_block;
    refd_local_defs.clear();
  }

  StatementBlock *StatementBlock::clone() const
  {
    FATAL_ERROR("StatementBlock::clone");
  }

  void StatementBlock::dump(unsigned int level) const
  {
    size_t n = stmts.size();
    DEBUG(level, "StatementBlock at %p with %lu", static_cast<const void*>(this),
      static_cast<unsigned long>( n ));
    for (size_t i = 0; i < n; ++i) {
      stmts[i]->dump(level+1);
    }
  }

  void StatementBlock::add_stmt(Statement *p_stmt, bool to_front)
  {
    if(!p_stmt)
      FATAL_ERROR("StatementBlock::add_stmt()");
    if (to_front) {
      stmts.add_front(p_stmt);
    } else {
      stmts.add(p_stmt);
    }
    p_stmt->set_my_scope(this);
    p_stmt->set_my_sb(this, stmts.size()-1);
  }

  void StatementBlock::set_my_scope(Scope *p_scope)
  {
    set_parent_scope(p_scope);
    for(size_t i=0; i<stmts.size(); i++)
      stmts[i]->set_my_scope(this);
    for (size_t i = 0; i < catch_blocks.size(); ++i) {
      catch_blocks[i]->set_parent_scope(p_scope);
    }
    if (finally_block != NULL) {
      finally_block->set_parent_scope(p_scope);
    }
  }

  void StatementBlock::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for(size_t i=0; i<stmts.size(); i++)
      stmts[i]->set_fullname(p_fullname+".stmt_"+Int2string(i+1));
    for (size_t i = 0; i < catch_blocks.size(); ++i) {
      catch_blocks[i]->set_fullname(p_fullname + ".catch_block_" + Int2string(i + 1));
    }
    if (finally_block != NULL) {
      finally_block->set_fullname(p_fullname + ".finally_block");
    }
  }

  /** \todo handle loops and conditional statements */
  Statement *StatementBlock::get_first_stmt() const
  {
    size_t nof_stmts = stmts.size();
    for (size_t i = 0; i < nof_stmts; i++) {
      Statement *stmt = stmts[i];
      switch (stmt->get_statementtype()) {
      case Statement::S_LABEL:
	// ignore and go to the next statement
	break;
      case Statement::S_BLOCK: {
	// if the block is not empty return its first statement
	// otherwise go to the next statement
	Statement *first_stmt = stmt->get_block()->get_first_stmt();
	if (first_stmt) return first_stmt;
	else break; }
      case Statement::S_DOWHILE: {
	// if the block is not empty return its first statement
	// otherwise return the whole do-while statement
	Statement *first_stmt = stmt->get_block()->get_first_stmt();
	if (first_stmt) return first_stmt;
	else return stmt; }
      default:
        return stmt;
      }
    }
    return 0;
  }

  void StatementBlock::register_def(Definition *p_def)
  {
    if(!p_def) FATAL_ERROR("StatementBlock::register_def()");
    const Identifier& id=p_def->get_id();
    if (defs.has_key(id)) {
      const char *dispname = id.get_dispname().c_str();
      p_def->error("Duplicate definition with identifier `%s'", dispname);
      defs[id]->note("Previous definition with identifier `%s' is here",
	dispname);
    } else {
      defs.add(id, p_def);
      if (parent_scope) {
	if (parent_scope->has_ass_withId(id)) {
    if (parent_scope->get_scope_class() == NULL) {
      const char *dispname = id.get_dispname().c_str();
      p_def->error("Definition with identifier `%s' is not unique"
                         " in the scope hierarchy", dispname);
      Reference ref(0, id.clone());
      Common::Assignment *ass = parent_scope->get_ass_bySRef(&ref);
      if (!ass) FATAL_ERROR("StatementBlock::register_def()");
      ass->note("Previous definition with identifier `%s' in higher "
                       "scope unit is here", dispname);
    }
	} else if (parent_scope->is_valid_moduleid(id)) {
	  p_def->warning("Definition with name `%s' hides a module identifier",
	    id.get_dispname().c_str());
	}
      }
    }
  }

  bool StatementBlock::has_ass_withId(const Identifier& p_id)
  {
    return defs.has_key(p_id) || get_parent_scope()->has_ass_withId(p_id);
  }

  Common::Assignment* StatementBlock::get_ass_bySRef(Ref_simple *p_ref)
  {
    if(p_ref->get_modid() || p_ref->get_reftype() != Ref_simple::REF_BASIC) {
      return get_parent_scope()->get_ass_bySRef(p_ref);
    }
    const Identifier& id=*p_ref->get_id();
    if(defs.has_key(id)) return defs[id];
    else return get_parent_scope()->get_ass_bySRef(p_ref);
  }

  Type *StatementBlock::get_mtc_system_comptype(bool is_system)
  {
    // return NULL outside test cases
    if (!my_def)
      return 0;
    Common::Assignment::asstype_t asstype = my_def->get_asstype();
    if (asstype == Common::Assignment::A_TESTCASE) {
      if (is_system) {
        Def_Testcase *t_tc = dynamic_cast<Def_Testcase*>(my_def);
        if (!t_tc) FATAL_ERROR("StatementBlock::get_mtc_system_comptype()");
        Type *system_ct = t_tc->get_SystemType();
        if (system_ct) return system_ct;
        // otherwise (if the testcase has no system clause) the type of 'system'
        // is the same as the type of 'mtc' (which is given in 'runs on' clause)
      }
      return my_def->get_RunsOnType();
    } else if (asstype == Common::Assignment::A_FUNCTION ||
               asstype == Common::Assignment::A_FUNCTION_RVAL ||
               asstype == Common::Assignment::A_FUNCTION_RTEMP ||
               asstype == Common::Assignment::A_ALTSTEP) {
      if (is_system) {
        return my_def->get_SystemType();
      } else {
        return my_def->get_MtcType();
      }
    } else {
      return 0;
    }
  }

  Ttcn::StatementBlock *StatementBlock::get_statementblock_scope()
  {
    return this;
  }

  void StatementBlock::set_my_sb(StatementBlock *p_sb, size_t p_index)
  {
    my_sb=p_sb;
    my_sb_index=p_index;
    for(size_t i=0; i<stmts.size(); i++)
      stmts[i]->set_my_sb(this, i);
  }

  size_t StatementBlock::get_my_sb_index() const
  {
    if(!my_sb) FATAL_ERROR("StatementBlock::get_my_sb_index()");
    return my_sb_index;
  }

  void StatementBlock::set_my_def(Definition *p_def)
  {
    my_def=p_def;
    for(size_t i=0; i<stmts.size(); i++)
      stmts[i]->set_my_def(p_def);
    for (size_t i = 0; i < catch_blocks.size(); ++i) {
      catch_blocks[i]->set_my_def(p_def);
    }
    if (finally_block != NULL) {
      finally_block->set_my_def(p_def);
    }
  }

  void StatementBlock::set_my_ags(AltGuards *p_ags)
  {
    for(size_t i=0; i<stmts.size(); i++)
      stmts[i]->set_my_ags(p_ags);
  }

  void StatementBlock::set_my_laic_stmt(AltGuards *p_ags,
                                        Statement *p_loop_stmt)
  {
    for(size_t i=0; i<stmts.size(); i++)
      stmts[i]->set_my_laic_stmt(p_ags, p_loop_stmt);
  }
  
  void StatementBlock::add_catch_block(StatementBlock* p_catch)
  {
    if (p_catch == NULL) {
      FATAL_ERROR("StatementBlock::add_catch_block");
    }
    p_catch->exception_handling = EH_OOP_CATCH;
    catch_blocks.add(p_catch);
  }
  
  void StatementBlock::set_finally_block(StatementBlock* p_finally)
  {
    if (finally_block != NULL || p_finally == NULL) {
      FATAL_ERROR("StatementBlock::set_finally_block");
    }
    p_finally->exception_handling = EH_OOP_FINALLY;
    finally_block = p_finally;
  }
  
  void StatementBlock::add_refd_local_def(Common::Assignment* p_def)
  {
    if (exception_handling != EH_OOP_FINALLY || p_def == NULL) {
      FATAL_ERROR("StatementBlock::add_refd_local_defs");
    }
    if (!refd_local_defs.has_key(p_def)) {
      refd_local_defs.add(p_def, NULL);
    }
  }
  
  boolean StatementBlock::is_in_finally_block() const
  {
    if (exception_handling == EH_OOP_FINALLY) {
      return TRUE;
    }
    if (my_sb != NULL) {
      return my_sb->is_in_finally_block();
    }
    return FALSE;
  }
  
  StatementBlock* StatementBlock::get_finally_block()
  {
    if (exception_handling == EH_OOP_FINALLY) {
      return this;
    }
    if (my_sb != NULL) {
      return my_sb->get_finally_block();
    }
    FATAL_ERROR("StatementBlock::get_finally_block()");
  }
  
  boolean StatementBlock::is_in_dynamic_template() const
  {
    if (dynamic_template != NULL) {
      return TRUE;
    }
    if (my_sb != NULL) {
      return my_sb->is_in_dynamic_template();
    }
    return FALSE;
  }
  
  Template* StatementBlock::get_dynamic_template()
  {
    if (dynamic_template != NULL) {
      return dynamic_template;
    }
    if (my_sb != NULL) {
      return my_sb->get_dynamic_template();
    }
    FATAL_ERROR("StatementBlock::get_dynamic_template()");
  }
  
  boolean StatementBlock::is_empty() const
  {
    return stmts.size() == 0 && exception_handling == StatementBlock::EH_NONE &&
      catch_blocks.size() == 0 && finally_block == NULL;
  }

  StatementBlock::returnstatus_t StatementBlock::has_return() const
  {
    returnstatus_t ret_val = RS_NO;
    size_t nof_stmts = stmts.size();
    for (size_t i = 0; i < nof_stmts && ret_val != RS_YES; i++) {
      Statement *stmt = stmts[i];
      if (stmt->get_statementtype() == Statement::S_GOTO) {
	if (stmt->goto_jumps_forward()) {
	  // heuristics without deep analysis of the control flow graph:
	  // skip over the next statements until a (used) label is found
	  // the behaviour will be sound (i.e. no false errors will be reported)
	  for (i++; i < nof_stmts; i++) {
	    stmt = stmts[i];
	    if (stmt->get_statementtype() == Statement::S_LABEL &&
		stmt->label_is_used()) break;
	  }
	} else ret_val = RS_YES;
      } else if (stmt->get_statementtype()==Statement::S_BLOCK && stmt->get_block()->exception_handling!=EH_NONE) {
        switch (stmt->get_block()->exception_handling) {
        case EH_TRY: // the i-th statement is a try{} statement block, the (i+1)-th must be a catch{} block
          if ((i+1)<nof_stmts && stmts[i+1]->get_statementtype()==Statement::S_BLOCK && stmts[i+1]->get_block()->exception_handling==EH_CATCH) {
            returnstatus_t try_block_rs = stmt->has_return();
            returnstatus_t catch_block_rs = stmts[i+1]->has_return();
            // 3 x 3 combinations
            if (try_block_rs==catch_block_rs) {
              switch (try_block_rs) {
              case RS_YES:
                ret_val = RS_YES;
                break;
              case RS_MAYBE:
                ret_val = RS_MAYBE;
                break;
              default:
                break;
              }
            } else {
              ret_val = RS_MAYBE;
            }
          } else { // if next statement is not a catch{} block then that error has already been reported
            ret_val = RS_MAYBE; // assume the catch block was an RS_MAYBE
          }
          break;
        case EH_CATCH:
          // logically this is part of the preceding try{} block, handle it as part of it, see above case EH_TRY
          break;
        default:
          FATAL_ERROR("StatementBlock::has_return()");
        }
      } else {
	switch (stmt->has_return()) {
	case RS_YES:
	  ret_val = RS_YES;
	  break;
	case RS_MAYBE:
	  ret_val = RS_MAYBE;
	  break;
	default:
	  break;
	}
      }
    }
    for (size_t i = 0; i < catch_blocks.size(); ++i) {
      // all 'catch' blocks must also have return for the statement block to have return
      switch(catch_blocks[i]->has_return()) {
      case RS_YES:
        break; // OK, keep checking
      case RS_MAYBE:
        return RS_MAYBE;
      case RS_NO:
        ret_val = (ret_val == RS_NO) ? RS_NO : RS_MAYBE;
        break;
      }
    }
    return ret_val;
  }

  bool StatementBlock::has_receiving_stmt(size_t start_i) const
  {
    for(size_t i=start_i; i<stmts.size(); i++)
      if(stmts[i]->has_receiving_stmt()) return true;
    return false;
  }

  bool StatementBlock::has_def_stmt_i(size_t start_i) const
  {
    for(size_t i=start_i; i<stmts.size(); i++)
      if(stmts[i]->get_statementtype()==Statement::S_DEF) return true;
    return false;
  }

  size_t StatementBlock::last_receiving_stmt_i() const
  {
    size_t nof_stmts = stmts.size();
    if (nof_stmts > 0) {
      size_t i = nof_stmts;
      do {
	if (stmts[--i]->has_receiving_stmt()) return i;
      } while (i > 0);
    }
    return nof_stmts;
  }

  StatementBlock *StatementBlock::get_parent_block() const
  {
    for (Scope *s = get_parent_scope(); s; s=s->get_parent_scope()) {
      StatementBlock *sb = dynamic_cast<StatementBlock*>(s);
      if (sb) return sb;
    }
    return 0;
  }

  void StatementBlock::chk_trycatch_blocks(Statement* s1, Statement* s2) {
    if (s1 && s1->get_statementtype()==Statement::S_BLOCK && s1->get_block()->get_exception_handling()==StatementBlock::EH_TRY) {
      if (!(s2 && s2->get_statementtype()==Statement::S_BLOCK && s2->get_block()->get_exception_handling()==StatementBlock::EH_CATCH)) {
        s1->error("`try' statement block must be followed by a `catch' block");
      }
    }
    if (s2 && s2->get_statementtype()==Statement::S_BLOCK && s2->get_block()->get_exception_handling()==StatementBlock::EH_CATCH) {
      if (!(s1 && s1->get_statementtype()==Statement::S_BLOCK && s1->get_block()->get_exception_handling()==StatementBlock::EH_TRY)) {
        s2->error("`catch' statement block must be preceded by a `try' block");
      }
    }
  }

  void StatementBlock::chk()
  {
    if (checked) return;
    chk_labels();
    bool unreach_found = false;
    size_t nof_stmts = stmts.size();
    Statement *prev_stmt = 0;
    for (size_t i = 0; i < nof_stmts; i++) {
      Statement *stmt = stmts[i];
      stmt->chk();
      if (!unreach_found && stmt->get_statementtype() != Statement::S_LABEL &&
	  prev_stmt && prev_stmt->is_terminating()) {
	// a statement is unreachable if:
	// - it is not a label (i.e. goto cannot jump to it)
	// - it is not the first statement of the block
	// - the previous statement terminates the control flow
	stmt->warning("Control never reaches this statement");
	unreach_found = true;
      }
      // check try-catch statement block usage
      chk_trycatch_blocks(prev_stmt, stmt);
      //
      prev_stmt = stmt;
    }
    chk_trycatch_blocks(prev_stmt, 0);
    chk_unused_labels();
    map<string, Definition> catch_types;
    for (size_t i = 0; i < catch_blocks.size(); ++i) {
      if (oop_features) {
        catch_blocks[i]->chk();
        Definition* def_exc = catch_blocks[i]->get_stmt_byIndex(0)->get_def();
        string catch_type_str = def_exc->get_Type()->get_exception_name();
        if (catch_types.has_key(catch_type_str)) {
          def_exc->error("Duplicate catch block definition for exception type `%s'", catch_type_str.c_str());
          catch_types[catch_type_str]->note("Catch block for the same exception type already defined here");
        }
        else {
          catch_types.add(catch_type_str, def_exc);
        }
      }
      else {
        catch_blocks[i]->error("The compiler option `-k' must be activated to use object-oriented features such as catch clauses.");
      }
    }
    catch_types.clear();
    if (finally_block != NULL) {
      finally_block->chk();
    }
    checked = true;
  }

  void StatementBlock::chk_allowed_interleave()
  {
    size_t nof_stmts = stmts.size();
    for (size_t i = 0; i < nof_stmts; i++)
      stmts[i]->chk_allowed_interleave();
    for (size_t i = 0; i < catch_blocks.size(); ++i) {
      catch_blocks[i]->chk_allowed_interleave();
    }
    if (finally_block != NULL) {
      finally_block->chk_allowed_interleave();
    }
  }

  void StatementBlock::chk_labels()
  {
    if(labels_checked) return;
    for(size_t i=0; i<stmts.size(); i++) {
      Statement *st=stmts[i];
      if(st->get_statementtype()!=Statement::S_LABEL) continue;
      const Identifier& labelid=st->get_labelid();
      if(has_label(labelid)) {
        const char *name=labelid.get_dispname().c_str();
        st->error("Duplicate label `%s'", name);
        Statement *st2=get_label(labelid);
        st2->note("Previous definition of label `%s' is here", name);
      }
      else labels.add(labelid, st);
    }
    for (size_t i = 0; i < catch_blocks.size(); ++i) {
      catch_blocks[i]->chk_labels();
    }
    if (finally_block != NULL) {
      finally_block->chk_labels();
    }
    labels_checked=true;
  }

  void StatementBlock::chk_unused_labels()
  {
    size_t nof_stmts = stmts.size();
    for (size_t i = 0; i < nof_stmts; i++) {
      Statement *stmt = stmts[i];
      if (stmt->get_statementtype() == Statement::S_LABEL &&
	  !stmt->label_is_used())
	stmt->warning("Label `%s' is defined, but not used",
	  stmt->get_labelid().get_dispname().c_str());
    }
    for (size_t i = 0; i < catch_blocks.size(); ++i) {
      catch_blocks[i]->chk_unused_labels();
    }
    if (finally_block != NULL) {
      finally_block->chk_unused_labels();
    }
  }

  bool StatementBlock::has_label(const Identifier& p_id) const
  {
    for (const StatementBlock *sb = this; sb; sb = sb->get_parent_block())
      if (sb->labels.has_key(p_id)) return true;
    return false;
  }

  Statement *StatementBlock::get_label(const Identifier& p_id) const
  {
    for (const StatementBlock *sb = this; sb; sb = sb->get_parent_block())
      if (sb->labels.has_key(p_id)) return sb->labels[p_id];
    FATAL_ERROR("StatementBlock::get_label()");
    return 0;
  }

  void StatementBlock::set_code_section(
    GovernedSimple::code_section_t p_code_section)
  {
    for(size_t i = 0; i < stmts.size(); i++)
      stmts[i]->set_code_section(p_code_section);
    for (size_t i = 0; i < catch_blocks.size(); ++i) {
      catch_blocks[i]->set_code_section(p_code_section);
    }
    if (finally_block != NULL) {
      finally_block->set_code_section(p_code_section);
    }
  }

  char* StatementBlock::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars,
                                      AltGuards* alt_guards /* = NULL */)
  {
    if (exception_handling==EH_TRY) {
      str = mputstr(str, "TTCN_TryBlock try_block;\n");
    }
    if (finally_block != NULL) {
#if __cplusplus < 201103L
      str = finally_block->generate_code_finally(str, def_glob_vars, src_glob_vars);
#else
      // C++11 version:
      string tmp_id = get_scope_mod_gen()->get_temporary_id();
      str = mputprintf(str,
        "FINALLY_CPP11 %s([&] {\n", tmp_id.c_str());
      str = finally_block->generate_code(str, def_glob_vars, src_glob_vars);
      str = mputstr(str, "});\n");
#endif
    }
    if (catch_blocks.size() > 0) {
      str = mputstr(str, "try {\n");
    }
    if (stmts.size()>0) {
      Statement* first_stmt = stmts[0];
      str = first_stmt->generate_code(str, def_glob_vars, src_glob_vars);
      if (exception_handling==EH_CATCH) {
        if (first_stmt->get_statementtype()!=Statement::S_DEF) FATAL_ERROR("StatementBlock::generate_code()");
        Definition* error_msg_def = first_stmt->get_def();
        string error_msg_name = error_msg_def->get_id().get_name();
        str = mputprintf(str, "%s = ttcn_error.get_message();\n", error_msg_name.c_str());
      }
    }
    for(size_t i=1; i<stmts.size(); i++) {
      str = stmts[i]->generate_code(str, def_glob_vars, src_glob_vars);
    }
    if (alt_guards != NULL && exception_handling == EH_NONE) {
      str = alt_guards->generate_code_altstep(str, def_glob_vars, src_glob_vars);
    }
    if (exception_handling == EH_OOP_CATCH && alt_guards != NULL) {
      // we are in an altstep's statement block
      // indicate that an alt branch has been chosen (since exceptions can only be raised from
      // the statement blocks of the alt branches)
      str = mputstr(str, "return ALT_YES;\n");
    }
    if (catch_blocks.size() > 0) {
      str = mputstr(str,
        "}\n" // end of the 'try' block
        "catch (EXCEPTION_BASE& exc_base) {\n");
      for (size_t i = 0; i < catch_blocks.size(); ++i) {
        Type* exc_type = catch_blocks[i]->get_stmt_byIndex(0)->get_def()->get_Type();
        Type* exc_type_last = exc_type->get_type_refd_last();
        if (exc_type_last->get_typetype() == Common::Type::T_CLASS) {
          ClassTypeBody* class_ = exc_type_last->get_class_type_body();
          str = mputprintf(str,
            "%sif (exc_base.is_class() && dynamic_cast<%s*>(exc_base.get_object()) != NULL) {\n",
            i > 0 ? "else " : "", class_->is_built_in() ? "OBJECT" : exc_type_last->get_genname_own(this).c_str());
        }
        else {
          str = mputprintf(str,
            "%sif (exc_base.is_type(\"%s\")) {\n",
            i > 0 ? "else " : "", exc_type->get_exception_name().c_str());
        }
        str = catch_blocks[i]->generate_code(str, def_glob_vars, src_glob_vars, alt_guards);
        str = mputstr(str, "}\n");
      }
      // if none of the exception types matched, then just re-throw the exception as if nothing happened
      str = mputstr(str, 
        "else { throw exc_base; }\n"
        "}\n"); // end of the catch block
    }
    return str;
  }
  
  char* StatementBlock::generate_code_finally(char* str, char*& def_glob_vars, char*& src_glob_vars)
  {
    // the finally block's code is generated into the destructor of a newly created class;
    // all local definitions, parameters and the TTCN_Location object are added to the class as members,
    // so the destructor can reach them
    if (exception_handling != EH_OOP_FINALLY) {
      FATAL_ERROR("StatementBlock::generate_code_finally");
    }
    string tmp_id = get_scope_mod_gen()->get_temporary_id();
    str = mputprintf(str,
      "class %s_finally {\n"
      "public:\n", tmp_id.c_str());
    
    if (include_location_info) {
      str = mputstr(str, "TTCN_Location& current_location;\n");
    }
    bool* is_const = NULL;
    bool* is_template = NULL;
    bool* is_shadowed = NULL;
    if (refd_local_defs.size() > 0) {
      is_const = new bool[refd_local_defs.size()];
      is_template = new bool[refd_local_defs.size()];
      is_shadowed = new bool[refd_local_defs.size()];
    }
    for (size_t i = 0; i < refd_local_defs.size(); ++i) {
      is_const[i] = false;
      is_template[i] = false;
      is_shadowed[i] = false;
      Common::Assignment* def = refd_local_defs.get_nth_key(i);
      switch (def->get_asstype()) {
      case Common::Assignment::A_PAR_TEMPL_IN:
        is_template[i] = true;
        // fall through
      case Common::Assignment::A_PAR_VAL:
      case Common::Assignment::A_PAR_VAL_IN: {
        FormalPar* fpar = dynamic_cast<FormalPar*>(def);
        if (fpar == NULL) {
          FATAL_ERROR("StatementBlock::generate_code");
        }
        if (fpar->get_used_as_lvalue()) {
          is_shadowed[i] = true;
        }
        else {
          is_const[i] = true;
        }
        break; }
      case Common::Assignment::A_CONST:
        is_const[i] = true;
        break;
      case Common::Assignment::A_TEMPLATE:
        is_const[i] = true;
        // fall through
      case Common::Assignment::A_VAR_TEMPLATE:
      case Common::Assignment::A_PAR_TEMPL_INOUT:
      case Common::Assignment::A_PAR_TEMPL_OUT:
        is_template[i] = true;
        break;
      default:
        break;
      }
      str = mputprintf(str, "%s%s& %s%s;\n",
        is_const[i] ? "const " : "",
        is_template[i] ? def->get_Type()->get_genname_template(my_sb).c_str() : def->get_Type()->get_genname_value(my_sb).c_str(),
        def->get_id().get_name().c_str(), is_shadowed[i] ? "_shadow" : "");
    }
    if (include_location_info || refd_local_defs.size() > 0) {
      str = mputprintf(str, "%s_finally(", tmp_id.c_str());
      if (include_location_info) {
        str = mputstr(str, "TTCN_Location& p_loc");
      }
      for (size_t i = 0; i < refd_local_defs.size(); ++i) {
        Common::Assignment* def = refd_local_defs.get_nth_key(i);
        str = mputprintf(str, "%s%s%s& p_%s",
          include_location_info || i > 0 ? ", " : "", is_const[i] ? "const " : "",
          is_template[i] ? def->get_Type()->get_genname_template(my_sb).c_str() : def->get_Type()->get_genname_value(my_sb).c_str(),
          def->get_id().get_name().c_str());
      }
      str = mputstr(str, "): ");
      if (include_location_info) {
        str = mputstr(str, "current_location(p_loc)");
      }
      for (size_t i = 0; i < refd_local_defs.size(); ++i) {
        Common::Assignment* def = refd_local_defs.get_nth_key(i);
        str = mputprintf(str, "%s%s%s(p_%s)", include_location_info || i > 0 ? ", " : "",
          def->get_id().get_name().c_str(), is_shadowed[i] ? "_shadow" : "", def->get_id().get_name().c_str());
      }
      str = mputstr(str, " { }\n");
    }
    str = mputprintf(str,
      "~%s_finally() {\n"
      "try {\n", tmp_id.c_str());
    str = generate_code(str, def_glob_vars, src_glob_vars);
    str = mputprintf(str,
      "} catch (...) {\n"
      "fprintf(stderr, \"Unhandled exception or dynamic test case error in a finally block. Terminating application.\\n\");\n"
      "exit(EXIT_FAILURE);\n"
      "}\n"
      "}\n"
      "};\n"
      "%s_finally %s",
      tmp_id.c_str(), tmp_id.c_str());
    if (include_location_info || refd_local_defs.size() > 0) {
      str = mputc(str, '(');
      if (include_location_info) {
        str = mputstr(str, "current_location");
      }
      for (size_t i = 0; i < refd_local_defs.size(); ++i) {
        Common::Assignment* def = refd_local_defs.get_nth_key(i);
        str = mputprintf(str, "%s%s%s", include_location_info || i > 0 ? ", " : "",
          def->get_id().get_name().c_str(), is_shadowed[i] ? "_shadow" : "");
      }
      str = mputc(str, ')');
    }
    delete[] is_const;
    delete[] is_template;
    delete[] is_shadowed;
    return mputstr(str, ";\n");
  }

  void StatementBlock::ilt_generate_code(ILT *ilt)
  {
    size_t nof_stmts = stmts.size();
    if (nof_stmts == 0) return;
    char*& str=ilt->get_out_branches();
    size_t last_recv_stmt_i=last_receiving_stmt_i();
    // has no receiving stmt
    if (last_recv_stmt_i == nof_stmts) {
      bool has_def=has_def_stmt_i();
      if(has_def) str=mputstr(str, "{\n");
      for(size_t i=0; i<nof_stmts; i++)
        str=stmts[i]->generate_code(str, ilt->get_out_def_glob_vars(),
          ilt->get_out_src_glob_vars());
      if(has_def) str=mputstr(str, "}\n");
      return;
    }
    for(size_t i=0; i<=last_recv_stmt_i; i++)
      stmts[i]->ilt_generate_code(ilt);
    // the last part which does not contain receiving stmt
    if(last_recv_stmt_i==nof_stmts-1) return;
    bool has_def=has_def_stmt_i(last_recv_stmt_i+1);
    if(has_def) str=mputstr(str, "{\n");
    for(size_t i=last_recv_stmt_i+1; i<nof_stmts; i++)
      str=stmts[i]->generate_code(str, ilt->get_out_def_glob_vars(),
        ilt->get_out_src_glob_vars());
    if(has_def) str=mputstr(str, "}\n");
  }

  void StatementBlock::set_parent_path(WithAttribPath* p_path)
  {
    for (size_t i = 0; i < stmts.size(); i++)
      stmts[i]->set_parent_path(p_path);
    for (size_t i = 0; i < catch_blocks.size(); ++i) {
      catch_blocks[i]->set_parent_path(p_path);
    }
    if (finally_block != NULL) {
      finally_block->set_parent_path(p_path);
    }
  }

  // =================================
  // ===== Statement
  // =================================

  void Statement::clean_up()
  {
    switch (statementtype) {
    case S_ERROR:
    case S_BREAK:
    case S_CONTINUE:
    case S_STOP_EXEC:
    case S_REPEAT:
    case S_START_PROFILER:
    case S_STOP_PROFILER:
      break;
    case S_START_UNDEF:
    case S_STOP_UNDEF:
      delete undefstartstop.ref;
      delete undefstartstop.val;
      break;
    case S_UNKNOWN_INSTANCE:
    case S_FUNCTION_INSTANCE:
    case S_ALTSTEP_INSTANCE:
    case S_ACTIVATE:
      delete ref_pard;
      break;
    case S_DEF:
      delete def;
      break;
    case S_ASSIGNMENT:
      delete ass;
      break;
    case S_BLOCK:
      delete block;
      break;
    case S_LOG:
    case S_ACTION:
    case S_STOP_TESTCASE:
      delete logargs;
      break;
    case S_LABEL:
      delete label.id;
      delete label.clabel;
      break;
    case S_GOTO:
      delete go_to.id;
      break;
    case S_IF:
      delete if_stmt.ics;
      delete if_stmt.elseblock;
      delete if_stmt.elseblock_location;
      break;
    case S_SELECT:
      delete select.expr;
      delete select.scs;
      break;
    case S_SELECTUNION:
      delete select_union.expr;
      delete select_union.sus;
      break;
    case S_SELECT_CLASS:
      delete select_class.ref;
      delete select_class.sccs;
      break;
    case S_FOR:
      if(loop.for_stmt.varinst)
        delete loop.for_stmt.init_varinst;
      else
        delete loop.for_stmt.init_ass;
      delete loop.for_stmt.finalexpr;
      delete loop.for_stmt.step;
      delete loop.block;
      if (loop.label_next)
        delete loop.label_next;
      if (loop.il_label_end)
        delete loop.il_label_end;
      break;
    case S_WHILE:
    case S_DOWHILE:
      delete loop.expr;
      delete loop.block;
      if (loop.label_next)
        delete loop.label_next;
      if (loop.il_label_end)
        delete loop.il_label_end;
      break;
    case S_ALT:
    case S_INTERLEAVE:
      delete ags;
      break;
    case S_RETURN:
      delete returnexpr.v;
      delete returnexpr.t;
      break;
    case S_DEACTIVATE:
      delete deactivate;
      break;
    case S_SEND:
      delete port_op.portref;
      delete port_op.s.sendpar;
      delete port_op.s.toclause;
      delete port_op.s.timestampredirect;
      break;
    case S_CALL:
      delete port_op.portref;
      delete port_op.s.sendpar;
      delete port_op.s.call.timer;
      delete port_op.s.toclause;
      delete port_op.s.call.body;
      delete port_op.s.timestampredirect;
      break;
    case S_REPLY:
      delete port_op.portref;
      delete port_op.s.sendpar;
      delete port_op.s.replyval;
      delete port_op.s.toclause;
      delete port_op.s.timestampredirect;
      break;
    case S_RAISE:
      delete port_op.portref;
      delete port_op.s.raise.signature_ref;
      delete port_op.s.sendpar;
      delete port_op.s.toclause;
      delete port_op.s.timestampredirect;
      break;
    case S_RECEIVE:
    case S_CHECK_RECEIVE:
    case S_TRIGGER:
      delete port_op.portref;
      delete port_op.r.rcvpar;
      delete port_op.r.fromclause;
      delete port_op.r.redirect.value;
      delete port_op.r.redirect.sender;
      delete port_op.r.redirect.index;
      delete port_op.r.redirect.timestamp;
      break;
    case S_GETCALL:
    case S_CHECK_GETCALL:
      delete port_op.portref;
      delete port_op.r.rcvpar;
      delete port_op.r.fromclause;
      delete port_op.r.redirect.param;
      delete port_op.r.redirect.sender;
      delete port_op.r.redirect.index;
      delete port_op.r.redirect.timestamp;
      break;
    case S_GETREPLY:
    case S_CHECK_GETREPLY:
      delete port_op.portref;
      delete port_op.r.rcvpar;
      delete port_op.r.getreply_valuematch;
      delete port_op.r.fromclause;
      delete port_op.r.redirect.value;
      delete port_op.r.redirect.param;
      delete port_op.r.redirect.sender;
      delete port_op.r.redirect.index;
      delete port_op.r.redirect.timestamp;
      break;
    case S_CATCH:
    case S_CHECK_CATCH:
      delete port_op.portref;
      delete port_op.r.ctch.signature_ref;
      delete port_op.r.rcvpar;
      delete port_op.r.fromclause;
      delete port_op.r.redirect.value;
      delete port_op.r.redirect.sender;
      delete port_op.r.redirect.index;
      delete port_op.r.redirect.timestamp;
      break;
    case S_CHECK:
      delete port_op.portref;
      delete port_op.r.fromclause;
      delete port_op.r.redirect.sender;
      delete port_op.r.redirect.index;
      delete port_op.r.redirect.timestamp;
      break;
    case S_CLEAR:
    case S_START_PORT:
    case S_STOP_PORT:
    case S_HALT:
      delete port_op.portref;
      break;
    case S_START_COMP:
      delete comp_op.compref;
      delete comp_op.funcinstref;
      break;
    case S_START_COMP_REFD:
      delete comp_op.compref;
      delete comp_op.derefered.value;
      delete comp_op.derefered.ap_list2;
      break;
    case S_STOP_COMP:
    case S_KILL:
      delete comp_op.compref;
      break;
    case S_KILLED:
      delete comp_op.compref;
      delete comp_op.index_redirect;
      break;
    case S_DONE:
      if (comp_op.compref) {
        delete comp_op.compref;
        delete comp_op.donereturn.donematch;
        delete comp_op.donereturn.redirect;
        delete comp_op.index_redirect;
      }
      break;
    case S_CONNECT:
    case S_MAP:
    case S_DISCONNECT:
    case S_UNMAP:
      delete config_op.compref1;
      delete config_op.portref1;
      delete config_op.compref2;
      delete config_op.portref2;
      delete config_op.ap_list;
      break;
    case S_START_TIMER:
      delete timer_op.timerref;
      delete timer_op.value;
      break;
    case S_TIMEOUT:
      delete timer_op.index_redirect;
      // no break
    case S_STOP_TIMER:
      delete timer_op.timerref;
      break;
    case S_SETVERDICT:
      delete setverdict.verdictval;
      delete setverdict.logargs;
      break;
    case S_TESTCASE_INSTANCE:
      delete testcase_inst.tcref;
      delete testcase_inst.timerval;
      break;
    case S_TESTCASE_INSTANCE_REFD:
      delete execute_refd.value;
      delete execute_refd.ap_list2;
      delete execute_refd.timerval;
      break;
    case S_ACTIVATE_REFD:
    case S_UNKNOWN_INVOKED:
    case S_FUNCTION_INVOKED:
    case S_ALTSTEP_INVOKED:
      delete fau_refd.value;
      delete fau_refd.ap_list2;
      break;
    case S_STRING2TTCN:
    case S_INT2ENUM:
      delete convert_op.val;
      delete convert_op.ref;
      break;
    case S_UPDATE:
      delete update_op.ref;
      delete update_op.w_attrib_path;
      delete update_op.err_attrib;
      break;
    case S_SETSTATE:
      delete setstate_op.val;
      delete setstate_op.ti;
      break;
    case S_SETENCODE:
      delete setencode_op.type;
      delete setencode_op.encoding;
      break;
    case S_RAISE_OOP:
      if (raise_op.checked) {
        delete raise_op.v;
      }
      else {
        delete raise_op.ti;
      }
      break;
    default:
      FATAL_ERROR("Statement::clean_up()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_REPEAT:
      ags=0;
    case S_ERROR:
    case S_STOP_EXEC:
    case S_START_PROFILER:
    case S_STOP_PROFILER:
      break;
    case S_BREAK:
    case S_CONTINUE:
      brk_cnt.loop_stmt=0;
      brk_cnt.ags=0;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Reference *p_ref, Value *p_val)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_START_UNDEF:
      if (!p_ref) FATAL_ERROR("Statement::Statement()");
      undefstartstop.ref=p_ref;
      undefstartstop.val=p_val;
      break;
    case S_STOP_UNDEF:
      if (!p_ref || p_val) FATAL_ERROR("Statement::Statement()");
      undefstartstop.ref=p_ref;
      undefstartstop.val=0;
      break;
    case S_TESTCASE_INSTANCE:
      if(!p_ref)
        FATAL_ERROR("Statement::Statement()");
      testcase_inst.tcref=p_ref;
      testcase_inst.timerval=p_val;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Reference *p_ref)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_UNKNOWN_INSTANCE:
    case S_FUNCTION_INSTANCE:
    case S_ALTSTEP_INSTANCE:
    case S_ACTIVATE:
      if(!p_ref)
        FATAL_ERROR("Statement::Statement()");
      ref_pard=p_ref;
      break;
    case S_CLEAR:
    case S_START_PORT:
    case S_STOP_PORT:
    case S_HALT:
      port_op.portref=p_ref;
      break;
    case S_STOP_TIMER:
      timer_op.timerref=p_ref;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Value *p_derefered_value,
                       ParsedActualParameters *p_ap_list)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_ACTIVATE_REFD:
    case S_UNKNOWN_INVOKED:
    case S_FUNCTION_INVOKED:
    case S_ALTSTEP_INVOKED:
      if(!p_derefered_value || !p_ap_list)
        FATAL_ERROR("Statement::Statement()");
      fau_refd.value = p_derefered_value;
      fau_refd.t_list1 = p_ap_list;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    }
  }

  Statement::Statement(statementtype_t p_st, Definition *p_def)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_DEF:
      if(!p_def)
        FATAL_ERROR("Statement::Statement()");
      def=p_def;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Assignment *p_ass)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_ASSIGNMENT:
      if(!p_ass)
        FATAL_ERROR("Statement::Statement()");
      ass=p_ass;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, StatementBlock *p_block)
    : statementtype(p_st), my_sb(0)
  {
    switch (statementtype) {
    case S_BLOCK:
      if (!p_block) FATAL_ERROR("Statement::Statement()");
      block = p_block;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, LogArguments *p_logargs)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_LOG:
    case S_ACTION:
    case S_STOP_TESTCASE:
      logargs=p_logargs;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Identifier *p_id)
    : statementtype(p_st), my_sb(0)
  {
    if (!p_id)
      FATAL_ERROR("Statement::Statement()");
    switch (statementtype) {
    case S_LABEL:
      label.id = p_id;
      label.stmt_idx = 0;
      label.clabel = 0;
      label.used = false;
      break;
    case S_GOTO:
      go_to.id = p_id;
      go_to.stmt_idx = 0;
      go_to.label = 0;
      go_to.jumps_forward = false;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, IfClauses *p_ics,
                       StatementBlock *p_block, Location *p_loc)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_IF:
      if(!p_ics)
        FATAL_ERROR("Statement::Statement()");
      if_stmt.ics=p_ics;
      if (p_block) {
	if (!p_loc) FATAL_ERROR("Statement::Statement()");
	if_stmt.elseblock = p_block;
	if_stmt.elseblock_location = p_loc;
      } else {
	if (p_loc) FATAL_ERROR("Statement::Statement()");
	if_stmt.elseblock = 0;
	if_stmt.elseblock_location = 0;
      }
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Value *p_expr, SelectCases *p_scs)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_SELECT:
      if(!p_expr || !p_scs)
        FATAL_ERROR("Statement::Statement()");
      select.expr=p_expr;
      select.scs=p_scs;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }
  
  Statement::Statement(statementtype_t p_st, Value *p_expr, SelectUnions *p_sus)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_SELECTUNION:
      if(!p_expr || !p_sus)
        FATAL_ERROR("Statement::Statement()");
      select_union.expr=p_expr;
      select_union.sus=p_sus;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }
    
  Statement::Statement(statementtype_t p_st, Reference *p_ref, SelectClassCases *p_sccs)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_SELECT_CLASS:
      if (p_ref == NULL || p_sccs == NULL) {
        FATAL_ERROR("Statement::Statement()");
      }
      select_class.ref = p_ref;
      select_class.sccs = p_sccs;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Definitions *p_defs,
                       Assignment *p_ass, Value *p_final,
                       Assignment *p_step, StatementBlock *p_block)
    : statementtype(p_st), my_sb(0)
  {
    switch (statementtype) {
    case S_FOR: // precisely one of p_defs, p_ass allowed
      if (p_defs) {
        // it came from a for loop which looked like this:
        // for (var integer foo:=1; foo<10; foo:=foo+1) ;
	if (p_ass) FATAL_ERROR("Statement::Statement()");
	loop.for_stmt.varinst = true;
	loop.for_stmt.init_varinst = p_defs;
      } else {
        // it came from a for loop which looked like this:
        // for (foo:=1; foo<10; foo:=foo+1) ;
	if (!p_ass) FATAL_ERROR("Statement::Statement()");
	loop.for_stmt.varinst = false;
	loop.for_stmt.init_ass = p_ass;
      }
      if(!p_final || !p_step || !p_block)
        FATAL_ERROR("Statement::Statement()");
      loop.for_stmt.finalexpr=p_final;
      loop.for_stmt.step=p_step;
      loop.block=p_block;
      loop.label_next=0;
      loop.il_label_end=0;
      loop.has_cnt=false;
      loop.has_brk=false;
      loop.has_cnt_in_ags=false;
      loop.iterate_once=false; // not used by for
      loop.is_ilt=false;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Value *p_val,
                       StatementBlock *p_block)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_WHILE:
    case S_DOWHILE:
      if(!p_val || !p_block)
        FATAL_ERROR("Statement::Statement()");
      loop.expr=p_val;
      loop.block=p_block;
      loop.label_next=0;
      loop.il_label_end=0;
      loop.has_cnt=false;
      loop.has_brk=false;
      loop.has_cnt_in_ags=false;
      loop.iterate_once=false; // used only by do-while
      loop.is_ilt=false;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, AltGuards *p_ags)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_ALT:
    case S_INTERLEAVE:
      if(!p_ags)
        FATAL_ERROR("Statement::Statement()");
      ags=p_ags;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Template *p_temp)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_RETURN:
      returnexpr.v=0;
      returnexpr.t=p_temp;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Value *p_val)
    : statementtype(p_st), my_sb(0)
  {
    switch (statementtype) {
    case S_STOP_COMP:
    case S_KILL:
      comp_op.compref=p_val;
      break;
    case S_DEACTIVATE:
      deactivate=p_val;
      break;
    case S_SETSTATE:
      setstate_op.val = p_val;
      setstate_op.ti = NULL;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }
  
  Statement::Statement(statementtype_t p_st, Value *p_val, bool p_any_from,
                       Reference* p_index_redirect)
    : statementtype(p_st), my_sb(0)
  {
    switch (statementtype) {
    case S_KILLED:
      if (p_val == NULL) {
        FATAL_ERROR("Statement::Statement()");
      }
      comp_op.compref = p_val;
      comp_op.any_from = p_any_from;
      comp_op.index_redirect = p_index_redirect;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Value *p_val,
                       LogArguments *p_logargs)
    : statementtype(p_st), my_sb(0)
  {
    if (!p_val || statementtype != S_SETVERDICT)
      FATAL_ERROR("Statement::Statement()");
    setverdict.verdictval = p_val;
    setverdict.logargs = p_logargs;
  }

  Statement::Statement(statementtype_t p_st, Reference *p_ref,
                       TemplateInstance *p_templinst, Value *p_val,
                       Reference* p_timestampredirect, bool p_translate)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_SEND:
      if((!p_ref && p_translate == false) || !p_templinst)
        FATAL_ERROR("Statement::Statement()");
      port_op.portref=p_ref;
      port_op.translate = p_translate;
      port_op.s.sendpar=p_templinst;
      port_op.s.toclause=p_val;
      port_op.s.timestampredirect = p_timestampredirect;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Reference *p_ref,
              TemplateInstance *p_templinst, Value *p_timerval,
              bool p_nowait, Value *p_toclause, Reference* p_timestampredirect,
              AltGuards *p_callbody)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_CALL:
      if(!p_ref || !p_templinst || (p_timerval && p_nowait))
        FATAL_ERROR("Statement::Statement()");
      port_op.portref=p_ref;
      port_op.s.sendpar=p_templinst;
      port_op.s.call.timer=p_timerval;
      port_op.s.call.nowait=p_nowait;
      port_op.s.toclause=p_toclause;
      port_op.s.call.body=p_callbody;
      port_op.s.timestampredirect = p_timestampredirect;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Reference *p_ref,
                       TemplateInstance *p_templinst, Value *p_replyval,
                       Value *p_toclause, Reference* p_timestampredirect)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_REPLY:
      if(!p_ref || !p_templinst)
        FATAL_ERROR("Statement::Statement()");
      port_op.portref=p_ref;
      port_op.s.sendpar=p_templinst;
      port_op.s.replyval=p_replyval;
      port_op.s.toclause=p_toclause;
      port_op.s.timestampredirect = p_timestampredirect;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Reference *p_ref,
                       Reference *p_sig, TemplateInstance *p_templinst,
                       Value *p_toclause, Reference* p_timestampredirect)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_RAISE:
      if(!p_ref || !p_templinst || !p_sig)
        FATAL_ERROR("Statement::Statement()");
      port_op.portref=p_ref;
      port_op.s.raise.signature_ref=p_sig;
      port_op.s.raise.signature=0;
      port_op.s.sendpar=p_templinst;
      port_op.s.toclause=p_toclause;
      port_op.s.timestampredirect = p_timestampredirect;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Reference *p_ref, bool p_anyfrom,
                       TemplateInstance *p_templinst,
                       TemplateInstance *p_fromclause,
                       ValueRedirect *p_redirectval, Reference *p_redirectsender,
                       Reference* p_redirectindex, Reference* p_timestamp_redirect,
                       bool p_translate)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_RECEIVE:
    case S_CHECK_RECEIVE:
    case S_TRIGGER:
      port_op.translate = p_translate;
      port_op.portref=p_ref;
      port_op.anyfrom = p_anyfrom;
      port_op.r.rcvpar=p_templinst;
      port_op.r.fromclause=p_fromclause;
      port_op.r.redirect.value=p_redirectval;
      port_op.r.redirect.param=0;
      port_op.r.redirect.sender=p_redirectsender;
      port_op.r.redirect.index = p_redirectindex;
      port_op.r.redirect.timestamp = p_timestamp_redirect;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Reference *p_ref, bool p_anyfrom,
                       TemplateInstance *p_templinst,
                       TemplateInstance *p_fromclause,
                       ParamRedirect *p_redirectparam,
                       Reference *p_redirectsender,
                       Reference* p_redirectindex,
                       Reference* p_timestamp_redirect)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_GETCALL:
    case S_CHECK_GETCALL:
      port_op.portref=p_ref;
      port_op.anyfrom = p_anyfrom;
      port_op.r.rcvpar=p_templinst;
      port_op.r.fromclause=p_fromclause;
      port_op.r.redirect.value=0;
      port_op.r.redirect.param=p_redirectparam;
      port_op.r.redirect.sender=p_redirectsender;
      port_op.r.redirect.index = p_redirectindex;
      port_op.r.redirect.timestamp = p_timestamp_redirect;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Reference *p_ref, bool p_anyfrom,
                       TemplateInstance *p_templinst,
                       TemplateInstance *p_valuematch,
                       TemplateInstance *p_fromclause,
                       ValueRedirect *p_redirectval, ParamRedirect *p_redirectparam,
                       Reference *p_redirectsender, Reference* p_redirectindex,
                       Reference* p_timestamp_redirect)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_GETREPLY:
    case S_CHECK_GETREPLY:
      if (!p_templinst && p_valuematch) FATAL_ERROR("Statement::Statement()");
      port_op.portref=p_ref;
      port_op.anyfrom = p_anyfrom;
      port_op.r.rcvpar=p_templinst;
      port_op.r.getreply_valuematch=p_valuematch;
      port_op.r.fromclause=p_fromclause;
      port_op.r.redirect.value=p_redirectval;
      port_op.r.redirect.param=p_redirectparam;
      port_op.r.redirect.sender=p_redirectsender;
      port_op.r.redirect.index = p_redirectindex;
      port_op.r.redirect.timestamp = p_timestamp_redirect;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Reference *p_ref, bool p_anyfrom,
                       Reference *p_sig, TemplateInstance *p_templinst,
                       bool p_timeout, TemplateInstance *p_fromclause,
                       ValueRedirect *p_redirectval, Reference *p_redirectsender,
                       Reference* p_redirectindex, Reference* p_timestamp_redirect)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_CATCH:
    case S_CHECK_CATCH:
      if (((p_sig || p_templinst) && p_timeout) ||
        (p_sig && !p_templinst) || (!p_sig && p_templinst))
	FATAL_ERROR("Statement::Statement()");
      port_op.portref=p_ref;
      port_op.anyfrom = p_anyfrom;
      port_op.r.ctch.signature_ref=p_sig;
      port_op.r.ctch.signature=0;
      port_op.r.rcvpar=p_templinst;
      port_op.r.ctch.timeout=p_timeout;
      port_op.r.ctch.in_call=false;
      port_op.r.ctch.call_has_timer=false;
      port_op.r.fromclause=p_fromclause;
      port_op.r.redirect.value=p_redirectval;
      port_op.r.redirect.param=0;
      port_op.r.redirect.sender=p_redirectsender;
      port_op.r.redirect.index = p_redirectindex;
      port_op.r.redirect.timestamp = p_timestamp_redirect;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Reference *p_ref, bool p_anyfrom,
                       TemplateInstance *p_fromclause,
                       Reference *p_redirectsender, Reference* p_redirectindex,
                       Reference* p_timestamp_redirect)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_CHECK:
      port_op.portref=p_ref; // may be NULL for "any port.check"
      port_op.anyfrom = p_anyfrom;
      port_op.r.fromclause=p_fromclause;
      port_op.r.redirect.value=0;
      port_op.r.redirect.param=0;
      port_op.r.redirect.sender=p_redirectsender;
      port_op.r.redirect.index = p_redirectindex;
      port_op.r.redirect.timestamp = p_timestamp_redirect;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }
  
  Statement::Statement(statementtype_t p_st, Reference *p_ref, bool p_any_from,
                       Reference* p_redirectindex)
    : statementtype(p_st), my_sb(0)
  {
    if (statementtype != S_TIMEOUT) {
      FATAL_ERROR("Statement::Statement()");
    }
    timer_op.timerref = p_ref;
    timer_op.any_from = p_any_from;
    timer_op.index_redirect = p_redirectindex;
  }

  Statement::Statement(statementtype_t p_st, Value *p_compref,
                        Value *p_derefered_value, ParsedActualParameters *p_ap_list)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_START_COMP_REFD:
      if(!p_compref || !p_derefered_value || !p_ap_list)
        FATAL_ERROR("Statement::Statement()");
      comp_op.compref = p_compref;
      comp_op.derefered.value = p_derefered_value;
      comp_op.derefered.t_list1 = p_ap_list;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    }
  }

  Statement::Statement(statementtype_t p_st, Value *p_compref,
                       TemplateInstance *p_donematch, ValueRedirect *p_redirect,
                       Reference* p_index_redirect, bool p_any_from)
    : statementtype(p_st), my_sb(0)
  {
    switch (statementtype) {
    case S_DONE:
      if (!p_compref) FATAL_ERROR("Statement::Statement()");
      comp_op.compref = p_compref;
      comp_op.donereturn.donematch = p_donematch;
      comp_op.donereturn.redirect = p_redirect;
      comp_op.index_redirect = p_index_redirect;
      comp_op.any_from = p_any_from;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, component_t p_anyall)
    : statementtype(p_st), my_sb(0)
  {
    switch (statementtype) {
    case S_DONE:
      comp_op.donereturn.donematch = NULL;
      comp_op.donereturn.redirect = NULL;
      // no break
    case S_KILLED:
      comp_op.compref = 0;
      comp_op.any_or_all = p_anyall;
      comp_op.any_from = false;
      comp_op.index_redirect = NULL;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st,
                       Value *p_compref1, Reference *p_portref1,
                       Value *p_compref2, Reference *p_portref2,
                       ParsedActualParameters* p_params)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_CONNECT:
    case S_MAP:
    case S_DISCONNECT:
    case S_UNMAP:
      if(!p_compref1 || !p_portref1 || !p_compref2 || !p_portref2)
        FATAL_ERROR("Statement::Statement()");
      config_op.compref1=p_compref1;
      config_op.portref1=p_portref1;
      config_op.compref2=p_compref2;
      config_op.portref2=p_portref2;
      config_op.translate=false;
      config_op.parsed_params=p_params;
      config_op.fp_list=NULL;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    } // switch statementtype
  }

  Statement::Statement(statementtype_t p_st, Value *p_derefered_value,
                        TemplateInstances *p_ap_list, Value *p_val)
    : statementtype(p_st), my_sb(0)
  {
    switch(statementtype) {
    case S_TESTCASE_INSTANCE_REFD:
      if(!p_derefered_value) FATAL_ERROR("Statement::Statement()");
      execute_refd.value = p_derefered_value;
      execute_refd.t_list1 = p_ap_list;
      execute_refd.timerval = p_val;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    }
  }

  Statement::Statement(statementtype_t p_st, Value* p_val, Reference* p_ref)
    : statementtype(p_st), my_sb(0)
  {
    switch (statementtype) {
    case S_STRING2TTCN:
    case S_INT2ENUM:
      if (p_val==NULL || p_ref==NULL) {
        FATAL_ERROR("Statement::Statement()");
      }
      convert_op.val = p_val;
      convert_op.ref = p_ref;
      break;
    case S_START_COMP:
      if (p_val==NULL || p_ref==NULL) {
        FATAL_ERROR("Statement::Statement()");
      }
      comp_op.compref = p_val;
      comp_op.funcinstref = p_ref;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    }
  }
  
  Statement::Statement(statementtype_t p_st, Reference* p_ref, MultiWithAttrib* p_attrib)
  : statementtype(p_st), my_sb(0)
  {
    switch (statementtype) {
    case S_UPDATE:
      if (p_ref == NULL) {
        FATAL_ERROR("Statement::Statement()");
      }
      update_op.ref = p_ref;
      if (p_attrib != NULL) {
        update_op.w_attrib_path = new WithAttribPath;
        update_op.w_attrib_path->set_with_attr(p_attrib);
      }
      else {
        update_op.w_attrib_path = NULL;
      }
      update_op.err_attrib = NULL;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    }
  }
  
  Statement::Statement(statementtype_t p_st, Value* p_val, TemplateInstance* p_ti)
  : statementtype(p_st), my_sb(0)
  {
    switch (statementtype) {
    case S_SETSTATE:
      if (p_val == NULL || p_ti == NULL) {
        FATAL_ERROR("Statement::Statement()");
      }
      setstate_op.val = p_val;
      setstate_op.ti = p_ti;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    }
  }
    
  Statement::Statement(statementtype_t p_st, Type* p_type, Value* p_encoding)
  : statementtype(p_st), my_sb(0)
  {
    switch (statementtype) {
    case S_SETENCODE:
      if (p_type == NULL || p_encoding == NULL) {
        FATAL_ERROR("Statement::Statement()");
      }
      setencode_op.type = p_type;
      setencode_op.encoding = p_encoding;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    }
  }
  
  Statement::Statement(statementtype_t p_st, TemplateInstance* p_ti)
  : statementtype(p_st), my_sb(0)
  {
    switch (statementtype) {
    case S_RAISE_OOP:
      if (p_ti == NULL) {
        FATAL_ERROR("Statement::Statement()");
      }
      raise_op.checked = false;
      raise_op.ti = p_ti;
      break;
    default:
      FATAL_ERROR("Statement::Statement()");
    }
  }

  Statement::~Statement()
  {
    clean_up();
  }

  Statement *Statement::clone() const
  {
    FATAL_ERROR("Statement::clone");
  }

  void Statement::dump(unsigned int level) const
  {
    DEBUG(level, "Statement at %p, a(n) %s", static_cast<const void *>(this),
          get_stmt_name());
    switch (statementtype) {
    case S_TESTCASE_INSTANCE:
    case S_TESTCASE_INSTANCE_REFD: {
      Common::Value *v = execute_refd.value;
      v->dump(level + 1);
    } break;
    case S_DEF:
      def->dump(level + 1);
      break;
    case S_ASSIGNMENT:
      ass->dump(level + 1);
      break;
    case S_BLOCK:
      block->dump(level + 1);
      break;
    case S_IF:
      if_stmt.ics->dump(level + 1);
      if (if_stmt.elseblock) if_stmt.elseblock->dump(level + 1);
      break;
    default:
      break;
    }
  }

  size_t Statement::get_my_sb_index() const
  {
    switch (statementtype) {
    case S_LABEL:
      return label.stmt_idx;
    case S_GOTO:
      return go_to.stmt_idx;
    default:
      FATAL_ERROR("Statement::get_my_sb_index()");
      return 0;
    }
  }

  const char *Statement::get_stmt_name() const
  {
    switch(statementtype) {
    case S_ERROR: return "<erroneous statement>";
    case S_START_UNDEF: return "start";
    case S_STOP_UNDEF: return "stop";
    case S_UNKNOWN_INSTANCE: return "function or altstep instance";
    case S_UNKNOWN_INVOKED: return "function or altstep type invocation";
    case S_DEF: return "definition";
    case S_ASSIGNMENT: return "assignment";
    case S_FUNCTION_INSTANCE: return "function instance";
    case S_FUNCTION_INVOKED: return "function type invocation";
    case S_BLOCK: return "statement block";
    case S_LOG: return "log";
    case S_LABEL: return "label";
    case S_GOTO: return "goto";
    case S_IF: return "if";
    case S_SELECT: return "select-case";
    case S_SELECTUNION: return "select-union";
    case S_SELECT_CLASS: return "select-class";
    case S_FOR: return "for";
    case S_WHILE: return "while";
    case S_DOWHILE: return "do-while";
    case S_BREAK: return "break";
    case S_CONTINUE: return "continue";
    case S_STOP_EXEC: return "stop";
    case S_STOP_TESTCASE: return "testcase.stop";
    case S_ALT: return "alt";
    case S_REPEAT: return "repeat";
    case S_INTERLEAVE: return "interleave";
    case S_ALTSTEP_INSTANCE: return "altstep instance";
    case S_ALTSTEP_INVOKED: return "altstep type invocation";
    case S_RETURN: return "return";
    case S_ACTIVATE:
    case S_ACTIVATE_REFD:
      return "activate";
    case S_DEACTIVATE: return "deactivate";
    case S_SEND: return "send";
    case S_CALL: return "call";
    case S_REPLY: return "reply";
    case S_RAISE: return "raise";
    case S_RECEIVE: return "receive";
    case S_TRIGGER: return "trigger";
    case S_GETCALL: return "getcall";
    case S_GETREPLY: return "getreply";
    case S_CATCH: return "catch";
    case S_CHECK: return "check";
    case S_CHECK_RECEIVE: return "check-receive";
    case S_CHECK_GETCALL: return "check-getcall";
    case S_CHECK_GETREPLY: return "check-getreply";
    case S_CHECK_CATCH: return "check-catch";
    case S_CLEAR: return "clear";
    case S_START_PORT: return "start port";
    case S_STOP_PORT: return "stop port";
    case S_HALT: return "halt";
    case S_START_COMP:
    case S_START_COMP_REFD:
      return "start test component";
    case S_STOP_COMP: return "stop test component";
    case S_DONE: return "done";
    case S_KILL: return "kill";
    case S_KILLED: return "killed";
    case S_CONNECT: return "connect";
    case S_MAP: return "map";
    case S_DISCONNECT: return "disconnect";
    case S_UNMAP: return "unmap";
    case S_START_TIMER: return "start timer";
    case S_STOP_TIMER: return "stop timer";
    case S_TIMEOUT: return "timeout";
    case S_SETVERDICT: return "setverdict";
    case S_ACTION: return "action";
    case S_TESTCASE_INSTANCE:
    case S_TESTCASE_INSTANCE_REFD:
      return "execute";
    case S_STRING2TTCN: return "string2ttcn";
    case S_INT2ENUM: return "int2enum";
    case S_START_PROFILER: return "@profiler.start";
    case S_STOP_PROFILER: return "@profiler.stop";
    case S_UPDATE: return "@update";
    case S_SETSTATE: return "setstate";
    case S_SETENCODE: return "setencode";
    default:
      FATAL_ERROR("Statement::get_stmt_name()");
      return "";
    } // switch statementtype
  }

  const Identifier& Statement::get_labelid() const
  {
    switch (statementtype) {
    case S_LABEL:
      return *label.id;
    case S_GOTO:
      return *go_to.id;
    default:
      FATAL_ERROR("Statement::get_labelid()");
      return *label.id;
    }
  }

  bool Statement::label_is_used() const
  {
    if (statementtype != S_LABEL) FATAL_ERROR("Statement::label_is_used()");
    return label.used;
  }

  bool Statement::goto_jumps_forward() const
  {
    if (statementtype != S_GOTO) FATAL_ERROR("Statement::goto_jumps_forward()");
    return go_to.jumps_forward;
  }

  const string& Statement::get_clabel()
  {
    if (statementtype != S_LABEL || !my_sb)
      FATAL_ERROR("Statement::get_clabel()");
    if (!label.clabel) label.clabel =
	new string(my_sb->get_scope_mod_gen()->get_temporary_id());
    return *label.clabel;
  }

  Definition *Statement::get_def() const
  {
    if (statementtype != S_DEF) FATAL_ERROR("Statement::get_def()");
    return def;
  }

  AltGuards *Statement::get_ags() const
  {
    if (statementtype != S_ALT && statementtype != S_INTERLEAVE)
      FATAL_ERROR("Statement::get_ags()");
    return ags;
  }

  StatementBlock *Statement::get_block() const
  {
    switch (statementtype) {
    case S_BLOCK:
      return block;
    case S_FOR:
    case S_WHILE:
    case S_DOWHILE:
      return loop.block;
    default:
      FATAL_ERROR("Statement::get_block()");
      return 0;
    }
  }

  void Statement::set_my_scope(Scope *p_scope)
  {
    switch(statementtype) {
    case S_ERROR:
    case S_LABEL:
    case S_GOTO:
    case S_BREAK:
    case S_CONTINUE:
    case S_STOP_EXEC:
    case S_REPEAT:
    case S_START_PROFILER:
    case S_STOP_PROFILER:
      break;
    case S_START_UNDEF:
    case S_STOP_UNDEF:
      undefstartstop.ref->set_my_scope(p_scope);
      if (undefstartstop.val) undefstartstop.val->set_my_scope(p_scope);
      break;
    case S_UNKNOWN_INSTANCE:
    case S_FUNCTION_INSTANCE:
    case S_ALTSTEP_INSTANCE:
    case S_ACTIVATE:
      ref_pard->set_my_scope(p_scope);
      break;
    case S_DEF:
      def->set_my_scope(p_scope);
      break;
    case S_ASSIGNMENT:
      ass->set_my_scope(p_scope);
      break;
    case S_BLOCK:
      block->set_my_scope(p_scope);
      break;
    case S_LOG:
    case S_ACTION:
    case S_STOP_TESTCASE:
      if (logargs) logargs->set_my_scope(p_scope);
      break;
    case S_IF:
      if_stmt.ics->set_my_scope(p_scope);
      if(if_stmt.elseblock) if_stmt.elseblock->set_my_scope(p_scope);
      break;
    case S_SELECT:
      select.expr->set_my_scope(p_scope);
      select.scs->set_my_scope(p_scope);
      break;
    case S_SELECTUNION:
      select_union.expr->set_my_scope(p_scope);
      select_union.sus->set_my_scope(p_scope);
      break;
    case S_SELECT_CLASS:
      select_class.ref->set_my_scope(p_scope);
      select_class.sccs->set_my_scope(p_scope);
      break;
    case S_FOR:
      if (loop.for_stmt.varinst) {
        loop.for_stmt.init_varinst->set_parent_scope(p_scope);
        loop.for_stmt.finalexpr->set_my_scope(loop.for_stmt.init_varinst);
        loop.for_stmt.step->set_my_scope(loop.for_stmt.init_varinst);
        loop.block->set_my_scope(loop.for_stmt.init_varinst);
      } else {
        loop.for_stmt.init_ass->set_my_scope(p_scope);
        loop.for_stmt.finalexpr->set_my_scope(p_scope);
        loop.for_stmt.step->set_my_scope(p_scope);
        loop.block->set_my_scope(p_scope);
      }
      break;
    case S_WHILE:
    case S_DOWHILE:
      loop.expr->set_my_scope(p_scope);
      loop.block->set_my_scope(p_scope);
      break;
    case S_ALT:
    case S_INTERLEAVE:
      ags->set_my_scope(p_scope);
      break;
    case S_RETURN:
      if (returnexpr.v) returnexpr.v->set_my_scope(p_scope);
      if (returnexpr.t) returnexpr.t->set_my_scope(p_scope);
      break;
    case S_DEACTIVATE:
      if (deactivate) deactivate->set_my_scope(p_scope);
      break;
    case S_SEND:
      if (!port_op.translate) port_op.portref->set_my_scope(p_scope);
      port_op.s.sendpar->set_my_scope(p_scope);
      if(port_op.s.toclause) port_op.s.toclause->set_my_scope(p_scope);
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_my_scope(p_scope);
      }
      break;
    case S_CALL:
      port_op.portref->set_my_scope(p_scope);
      port_op.s.sendpar->set_my_scope(p_scope);
      if(port_op.s.toclause) port_op.s.toclause->set_my_scope(p_scope);
      if(port_op.s.call.timer) port_op.s.call.timer->set_my_scope(p_scope);
      if(port_op.s.call.body) port_op.s.call.body->set_my_scope(p_scope);
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_my_scope(p_scope);
      }
      break;
    case S_REPLY:
      port_op.portref->set_my_scope(p_scope);
      port_op.s.sendpar->set_my_scope(p_scope);
      if(port_op.s.replyval) port_op.s.replyval->set_my_scope(p_scope);
      if(port_op.s.toclause) port_op.s.toclause->set_my_scope(p_scope);
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_my_scope(p_scope);
      }
      break;
    case S_RAISE:
      port_op.portref->set_my_scope(p_scope);
      port_op.s.raise.signature_ref->set_my_scope(p_scope);
      port_op.s.sendpar->set_my_scope(p_scope);
      if(port_op.s.toclause) port_op.s.toclause->set_my_scope(p_scope);
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_my_scope(p_scope);
      }
      break;
    case S_RECEIVE:
    case S_CHECK_RECEIVE:
    case S_TRIGGER:
      if(port_op.portref) port_op.portref->set_my_scope(p_scope);
      if(port_op.r.rcvpar) port_op.r.rcvpar->set_my_scope(p_scope);
      if(port_op.r.fromclause) port_op.r.fromclause->set_my_scope(p_scope);
      if(port_op.r.redirect.value)
        port_op.r.redirect.value->set_my_scope(p_scope);
      if(port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_my_scope(p_scope);
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_my_scope(p_scope);
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_my_scope(p_scope);
      }
      break;
    case S_GETCALL:
    case S_CHECK_GETCALL:
      if(port_op.portref) port_op.portref->set_my_scope(p_scope);
      if(port_op.r.rcvpar) port_op.r.rcvpar->set_my_scope(p_scope);
      if(port_op.r.fromclause) port_op.r.fromclause->set_my_scope(p_scope);
      if(port_op.r.redirect.param)
        port_op.r.redirect.param->set_my_scope(p_scope);
      if(port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_my_scope(p_scope);
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_my_scope(p_scope);
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_my_scope(p_scope);
      }
      break;
    case S_GETREPLY:
    case S_CHECK_GETREPLY:
      if(port_op.portref) port_op.portref->set_my_scope(p_scope);
      if(port_op.r.rcvpar) port_op.r.rcvpar->set_my_scope(p_scope);
      if(port_op.r.getreply_valuematch)
        port_op.r.getreply_valuematch->set_my_scope(p_scope);
      if(port_op.r.fromclause) port_op.r.fromclause->set_my_scope(p_scope);
      if(port_op.r.redirect.value)
        port_op.r.redirect.value->set_my_scope(p_scope);
      if(port_op.r.redirect.param)
        port_op.r.redirect.param->set_my_scope(p_scope);
      if(port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_my_scope(p_scope);
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_my_scope(p_scope);
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_my_scope(p_scope);
      }
      break;
    case S_CATCH:
    case S_CHECK_CATCH:
      if(port_op.portref) port_op.portref->set_my_scope(p_scope);
      if(port_op.r.ctch.signature_ref)
        port_op.r.ctch.signature_ref->set_my_scope(p_scope);
      if(port_op.r.rcvpar) port_op.r.rcvpar->set_my_scope(p_scope);
      if(port_op.r.fromclause) port_op.r.fromclause->set_my_scope(p_scope);
      if(port_op.r.redirect.value)
        port_op.r.redirect.value->set_my_scope(p_scope);
      if(port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_my_scope(p_scope);
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_my_scope(p_scope);
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_my_scope(p_scope);
      }
      break;
    case S_CHECK:
      if(port_op.portref) port_op.portref->set_my_scope(p_scope);
      if(port_op.r.fromclause) port_op.r.fromclause->set_my_scope(p_scope);
      if(port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_my_scope(p_scope);
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_my_scope(p_scope);
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_my_scope(p_scope);
      }
      break;
    case S_CLEAR:
    case S_START_PORT:
    case S_STOP_PORT:
    case S_HALT:
      if (port_op.portref) port_op.portref->set_my_scope(p_scope);
      break;
    case S_START_COMP:
      comp_op.compref->set_my_scope(p_scope);
      comp_op.funcinstref->set_my_scope(p_scope);
      break;
    case S_START_COMP_REFD:
      comp_op.compref->set_my_scope(p_scope);
      comp_op.derefered.value->set_my_scope(p_scope);
      comp_op.derefered.t_list1->set_my_scope(p_scope);
      break;
    case S_STOP_COMP:
    case S_KILL:
      if (comp_op.compref) comp_op.compref->set_my_scope(p_scope);
      break;
    case S_KILLED:
      if (comp_op.compref) comp_op.compref->set_my_scope(p_scope);
      if (comp_op.index_redirect != NULL) {
        comp_op.index_redirect->set_my_scope(p_scope);
      }
      break;
    case S_DONE:
      if (comp_op.compref) {
        comp_op.compref->set_my_scope(p_scope);
        if (comp_op.donereturn.donematch)
          comp_op.donereturn.donematch->set_my_scope(p_scope);
        if (comp_op.donereturn.redirect)
          comp_op.donereturn.redirect->set_my_scope(p_scope);
        if (comp_op.index_redirect != NULL) {
          comp_op.index_redirect->set_my_scope(p_scope);
        }
      }
      break;
    case S_CONNECT:
    case S_MAP:
    case S_DISCONNECT:
    case S_UNMAP:
      config_op.compref1->set_my_scope(p_scope);
      config_op.portref1->set_my_scope(p_scope);
      config_op.compref2->set_my_scope(p_scope);
      config_op.portref2->set_my_scope(p_scope);
      if (config_op.parsed_params != NULL) {
        config_op.parsed_params->set_my_scope(p_scope);
      }
      break;
    case S_START_TIMER:
      timer_op.timerref->set_my_scope(p_scope);
      if (timer_op.value) timer_op.value->set_my_scope(p_scope);
      break;
    case S_TIMEOUT:
      if (timer_op.index_redirect != NULL) {
        timer_op.index_redirect->set_my_scope(p_scope);
      }
      // no break
    case S_STOP_TIMER:
      if (timer_op.timerref) timer_op.timerref->set_my_scope(p_scope);
      break;
    case S_SETVERDICT:
      setverdict.verdictval->set_my_scope(p_scope);
      if (setverdict.logargs)
        setverdict.logargs->set_my_scope(p_scope);
      break;
    case S_TESTCASE_INSTANCE:
      testcase_inst.tcref->set_my_scope(p_scope);
      if (testcase_inst.timerval) testcase_inst.timerval->set_my_scope(p_scope);
      break;
    case S_TESTCASE_INSTANCE_REFD:
      execute_refd.value->set_my_scope(p_scope);
      execute_refd.t_list1->set_my_scope(p_scope);
      if(execute_refd.timerval) execute_refd.timerval->set_my_scope(p_scope);
      break;
    case S_ACTIVATE_REFD:
    case S_UNKNOWN_INVOKED:
    case S_FUNCTION_INVOKED:
    case S_ALTSTEP_INVOKED:
      fau_refd.value->set_my_scope(p_scope);
      fau_refd.t_list1->set_my_scope(p_scope);
      break;
    case S_STRING2TTCN:
    case S_INT2ENUM:
      convert_op.val->set_my_scope(p_scope);
      convert_op.ref->set_my_scope(p_scope);
      break;
    case S_UPDATE:
      update_op.ref->set_my_scope(p_scope);
      if (update_op.w_attrib_path != NULL) {
        update_op.w_attrib_path->set_my_scope(p_scope);
      }
      break;
    case S_SETSTATE:
      setstate_op.val->set_my_scope(p_scope);
      if (setstate_op.ti != NULL) {
        setstate_op.ti->set_my_scope(p_scope);
      }
      break;
    case S_SETENCODE:
      setencode_op.type->set_my_scope(p_scope);
      setencode_op.encoding->set_my_scope(p_scope);
      break;
    case S_RAISE_OOP:
      if (raise_op.checked) {
        FATAL_ERROR("Statement::set_my_scope()");
      }
      raise_op.ti->set_my_scope(p_scope);
      break;
    default:
      FATAL_ERROR("Statement::set_my_scope()");
    } // switch statementtype
  }

  void Statement::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    switch (statementtype) {
    case S_ERROR:
    case S_LABEL:
    case S_GOTO:
    case S_BREAK:
    case S_CONTINUE:
    case S_STOP_EXEC:
    case S_REPEAT:
    case S_START_PROFILER:
    case S_STOP_PROFILER:
      break;
    case S_START_UNDEF:
    case S_STOP_UNDEF:
      undefstartstop.ref->set_fullname(p_fullname+".ref");
      if (undefstartstop.val)
        undefstartstop.val->set_fullname(p_fullname+".val");
      break;
    case S_UNKNOWN_INSTANCE:
    case S_FUNCTION_INSTANCE:
    case S_ALTSTEP_INSTANCE:
    case S_ACTIVATE:
      ref_pard->set_fullname(p_fullname+".ref");
      break;
    case S_DEF:
      def->set_fullname(p_fullname+".def");
      break;
    case S_ASSIGNMENT:
      ass->set_fullname(p_fullname+".ass");
      break;
    case S_BLOCK:
      block->set_fullname(p_fullname+".block");
      break;
    case S_LOG:
    case S_ACTION:
    case S_STOP_TESTCASE:
      if (logargs) logargs->set_fullname(p_fullname+".logargs");
      break;
    case S_IF:
      if_stmt.ics->set_fullname(p_fullname+".ifclauses");
      if (if_stmt.elseblock)
        if_stmt.elseblock->set_fullname(p_fullname+".elseblock");
      break;
    case S_SELECT:
      select.expr->set_fullname(p_fullname+".expr");
      select.scs->set_fullname(p_fullname+".scs");
      break;
    case S_SELECTUNION:
      select_union.expr->set_fullname(p_fullname+".expr");
      select_union.sus->set_fullname(p_fullname+".sus");
      break;
    case S_SELECT_CLASS:
      select_class.ref->set_fullname(p_fullname+".ref");
      select_class.sccs->set_fullname(p_fullname+".sccs");
      break;
    case S_FOR:
      if(loop.for_stmt.varinst)
        loop.for_stmt.init_varinst->set_fullname(p_fullname+".init");
      else
        loop.for_stmt.init_ass->set_fullname(p_fullname+".init");
      loop.for_stmt.finalexpr->set_fullname(p_fullname+".final");
      loop.for_stmt.step->set_fullname(p_fullname+".step");
      loop.block->set_fullname(p_fullname+".block");
      break;
    case S_WHILE:
    case S_DOWHILE:
      loop.expr->set_fullname(p_fullname+".expr");
      loop.block->set_fullname(p_fullname+".block");
      break;
    case S_ALT:
    case S_INTERLEAVE:
      ags->set_fullname(p_fullname+".ags");
      break;
    case S_RETURN:
      if (returnexpr.v) returnexpr.v->set_fullname(p_fullname+".returnexpr");
      if (returnexpr.t) returnexpr.t->set_fullname(p_fullname+".returnexpr");
      break;
    case S_DEACTIVATE:
      if (deactivate) deactivate->set_fullname(p_fullname+".deact");
      break;
    case S_SEND:
      if (!port_op.translate) port_op.portref->set_fullname(p_fullname+".portref");
      port_op.s.sendpar->set_fullname(p_fullname+".sendpar");
      if(port_op.s.toclause)
        port_op.s.toclause->set_fullname(p_fullname+".to");
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_fullname(p_fullname + ".redirtimestamp");
      }
      break;
    case S_CALL:
      port_op.portref->set_fullname(p_fullname+".portref");
      port_op.s.sendpar->set_fullname(p_fullname+".sendpar");
      if(port_op.s.toclause)
        port_op.s.toclause->set_fullname(p_fullname+".to");
      if(port_op.s.call.timer)
        port_op.s.call.timer->set_fullname(p_fullname+".timer");
      if(port_op.s.call.body)
        port_op.s.call.body->set_fullname(p_fullname+".body");
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_fullname(p_fullname + ".redirtimestamp");
      }
      break;
    case S_REPLY:
      port_op.portref->set_fullname(p_fullname+".portref");
      port_op.s.sendpar->set_fullname(p_fullname+".sendpar");
      if(port_op.s.replyval)
        port_op.s.replyval->set_fullname(p_fullname+".replyval");
      if(port_op.s.toclause)
        port_op.s.toclause->set_fullname(p_fullname+".to");
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_fullname(p_fullname + ".redirtimestamp");
      }
      break;
    case S_RAISE:
      port_op.portref->set_fullname(p_fullname+".portref");
      port_op.s.raise.signature_ref->set_fullname(p_fullname+".sign");
      port_op.s.sendpar->set_fullname(p_fullname+".sendpar");
      if(port_op.s.toclause)
        port_op.s.toclause->set_fullname(p_fullname+".to");
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_fullname(p_fullname + ".redirtimestamp");
      }
      break;
    case S_RECEIVE:
    case S_CHECK_RECEIVE:
    case S_TRIGGER:
      if(port_op.portref) port_op.portref->set_fullname(p_fullname+".portref");
      if(port_op.r.rcvpar)
        port_op.r.rcvpar->set_fullname(p_fullname+".rcvpar");
      if(port_op.r.fromclause)
        port_op.r.fromclause->set_fullname(p_fullname+".from");
      if(port_op.r.redirect.value)
        port_op.r.redirect.value->set_fullname(p_fullname+".redirval");
      if(port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_fullname(p_fullname+".redirsender");
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_fullname(p_fullname+".redirindex");
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_fullname(p_fullname+".redirtimestamp");
      }
      break;
    case S_GETCALL:
    case S_CHECK_GETCALL:
      if(port_op.portref) port_op.portref->set_fullname(p_fullname+".portref");
      if(port_op.r.rcvpar)
        port_op.r.rcvpar->set_fullname(p_fullname+".rcvpar");
      if(port_op.r.fromclause)
        port_op.r.fromclause->set_fullname(p_fullname+".from");
      if(port_op.r.redirect.param)
        port_op.r.redirect.param->set_fullname(p_fullname+".pars");
      if(port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_fullname(p_fullname+".redirsender");
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_fullname(p_fullname+".redirindex");
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_fullname(p_fullname+".redirtimestamp");
      }
      break;
    case S_GETREPLY:
    case S_CHECK_GETREPLY:
      if(port_op.portref) port_op.portref->set_fullname(p_fullname+".portref");
      if(port_op.r.rcvpar)
        port_op.r.rcvpar->set_fullname(p_fullname+".rcvpar");
      if(port_op.r.getreply_valuematch)
        port_op.r.getreply_valuematch->set_fullname(p_fullname+".valmatch");
      if(port_op.r.fromclause)
        port_op.r.fromclause->set_fullname(p_fullname+".from");
      if(port_op.r.redirect.value)
        port_op.r.redirect.value->set_fullname(p_fullname+".redirval");
      if(port_op.r.redirect.param)
        port_op.r.redirect.param->set_fullname(p_fullname+".pars");
      if(port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_fullname(p_fullname+".redirsender");
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_fullname(p_fullname+".redirindex");
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_fullname(p_fullname+".redirtimestamp");
      }
      break;
    case S_CATCH:
    case S_CHECK_CATCH:
      if(port_op.portref) port_op.portref->set_fullname(p_fullname+".portref");
      if(port_op.r.ctch.signature_ref)
        port_op.r.ctch.signature_ref->set_fullname(p_fullname+".sign");
      if(port_op.r.rcvpar)
        port_op.r.rcvpar->set_fullname(p_fullname+".rcvpar");
      if(port_op.r.fromclause)
        port_op.r.fromclause->set_fullname(p_fullname+".from");
      if(port_op.r.redirect.value)
        port_op.r.redirect.value->set_fullname(p_fullname+".redirval");
      if(port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_fullname(p_fullname+".redirsender");
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_fullname(p_fullname+".redirindex");
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_fullname(p_fullname+".redirtimestamp");
      }
      break;
    case S_CHECK:
      if(port_op.portref) port_op.portref->set_fullname(p_fullname+".portref");
      if(port_op.r.fromclause)
        port_op.r.fromclause->set_fullname(p_fullname+".from");
      if(port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_fullname(p_fullname+".redirsender");
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_fullname(p_fullname+".redirindex");
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_fullname(p_fullname+".redirtimestamp");
      }
      break;
    case S_CLEAR:
    case S_START_PORT:
    case S_STOP_PORT:
    case S_HALT:
      if(port_op.portref) port_op.portref->set_fullname(p_fullname+".portref");
      break;
    case S_START_COMP:
      comp_op.compref->set_fullname(p_fullname+".compref");
      comp_op.funcinstref->set_fullname(p_fullname+".funcref");
      break;
    case S_START_COMP_REFD:
      comp_op.compref->set_fullname(p_fullname+".compref");
      comp_op.derefered.value->set_fullname(p_fullname+".funcref");
      comp_op.derefered.t_list1->set_fullname(p_fullname+".<parameters>");
      break;
    case S_STOP_COMP:
    case S_KILL:
      if (comp_op.compref) comp_op.compref->set_fullname(p_fullname+".compref");
      break;
    case S_KILLED:
      if (comp_op.compref) comp_op.compref->set_fullname(p_fullname+".compref");
      if (comp_op.index_redirect != NULL) {
        comp_op.index_redirect->set_fullname(p_fullname + ".redirindex");
      }
      break;
    case S_DONE:
      if(comp_op.compref) {
        comp_op.compref->set_fullname(p_fullname+".compref");
        if(comp_op.donereturn.donematch)
          comp_op.donereturn.donematch->set_fullname(p_fullname+".donematch");
        if(comp_op.donereturn.redirect)
          comp_op.donereturn.redirect->set_fullname(p_fullname+".redir");
        if (comp_op.index_redirect != NULL) {
          comp_op.index_redirect->set_fullname(p_fullname + ".redirindex");
        }
      }
      break;
    case S_CONNECT:
    case S_MAP:
    case S_DISCONNECT:
    case S_UNMAP:
      config_op.compref1->set_fullname(p_fullname+".compref1");
      config_op.portref1->set_fullname(p_fullname+".portref1");
      config_op.compref2->set_fullname(p_fullname+".compref2");
      config_op.portref2->set_fullname(p_fullname+".portref2");
      if (config_op.parsed_params != NULL) {
        config_op.parsed_params->set_fullname(p_fullname+".<parameters>");
      }
      break;
    case S_START_TIMER:
      timer_op.timerref->set_fullname(p_fullname+".timerref");
      if(timer_op.value) timer_op.value->set_fullname(p_fullname+".timerval");
      break;
    case S_TIMEOUT:
      if (timer_op.index_redirect != NULL) {
        timer_op.index_redirect->set_fullname(p_fullname + ".redirindex");
      }
      // no break
    case S_STOP_TIMER:
      if (timer_op.timerref)
        timer_op.timerref->set_fullname(p_fullname+".timerref");
      break;
    case S_SETVERDICT:
      setverdict.verdictval->set_fullname(p_fullname+".verdictval");
      if (setverdict.logargs)
        setverdict.logargs->set_fullname(p_fullname+".verdictreason");
      break;
    case S_TESTCASE_INSTANCE:
      testcase_inst.tcref->set_fullname(p_fullname+".tcref");
      if (testcase_inst.timerval)
        testcase_inst.timerval->set_fullname(p_fullname+".timerval");
      break;
    case S_TESTCASE_INSTANCE_REFD:
      execute_refd.value->set_fullname(p_fullname+".tcref");
      execute_refd.t_list1->set_fullname(p_fullname+".<parameters>");
      if(execute_refd.timerval)
        execute_refd.timerval->set_fullname(p_fullname+".timerval");
      break;
    case S_ACTIVATE_REFD:
    case S_UNKNOWN_INVOKED:
    case S_FUNCTION_INVOKED:
    case S_ALTSTEP_INVOKED:
      fau_refd.value->set_fullname(p_fullname+".ref");
      fau_refd.t_list1->set_fullname(p_fullname+".<parameters>");
      break;
    case S_STRING2TTCN:
    case S_INT2ENUM:
      convert_op.val->set_fullname(p_fullname+".ti");
      convert_op.ref->set_fullname(p_fullname+".ref");
      break;
    case S_UPDATE:
      update_op.ref->set_fullname(p_fullname + ".ref");
      if (update_op.w_attrib_path != NULL) {
        update_op.w_attrib_path->set_fullname(p_fullname + ".<attribpath>");
      }
      break;
    case S_SETSTATE:
      setstate_op.val->set_fullname(p_fullname + ".val");
      if (setstate_op.ti != NULL) {
        setstate_op.ti->set_fullname(p_fullname + ".ti");
      }
      break;
    case S_SETENCODE:
      setencode_op.type->set_fullname(p_fullname + ".type");
      setencode_op.encoding->set_fullname(p_fullname + ".encoding");
      break;
    case S_RAISE_OOP:
      if (raise_op.checked) {
        FATAL_ERROR("Statement::set_fullname()");
      }
      raise_op.ti->set_fullname(p_fullname + ".exception");
      break;
    default:
      FATAL_ERROR("Statement::set_fullname()");
    } // switch statementtype
  }

  void Statement::set_my_sb(StatementBlock *p_sb, size_t p_index)
  {
    my_sb=p_sb;
    switch(statementtype) {
    case S_BLOCK:
      block->set_my_sb(p_sb, p_index);
      break;
    case S_LABEL:
      label.stmt_idx = p_index;
      break;
    case S_GOTO:
      go_to.stmt_idx = p_index;
      break;
    case S_IF:
      if_stmt.ics->set_my_sb(p_sb, p_index);
      if(if_stmt.elseblock) if_stmt.elseblock->set_my_sb(p_sb, p_index);
      break;
    case S_SELECT:
      select.scs->set_my_sb(p_sb, p_index);
      break;
    case S_SELECTUNION:
      select_union.sus->set_my_sb(p_sb, p_index);
      break;
    case S_SELECT_CLASS:
      select_class.sccs->set_my_sb(p_sb, p_index);
      break;
    case S_FOR:
    case S_WHILE:
    case S_DOWHILE:
      loop.block->set_my_sb(p_sb, p_index);
      break;
    case S_ALT:
    case S_INTERLEAVE:
      ags->set_my_sb(p_sb, p_index);
      break;
    case S_CALL:
      if(port_op.s.call.body) port_op.s.call.body->set_my_sb(p_sb, p_index);
      break;
    default:
      break;
    } // switch statementtype
  }

  void Statement::set_my_def(Definition *p_def)
  {
    switch (statementtype) {
    case S_BLOCK:
      block->set_my_def(p_def);
      break;
    case S_IF:
      if_stmt.ics->set_my_def(p_def);
      if (if_stmt.elseblock) if_stmt.elseblock->set_my_def(p_def);
      break;
    case S_SELECT:
      select.scs->set_my_def(p_def);
      break;
    case S_SELECTUNION:
      select_union.sus->set_my_def(p_def);
      break;
    case S_SELECT_CLASS:
      select_class.sccs->set_my_def(p_def);
      break;
    case S_FOR:
    case S_WHILE:
    case S_DOWHILE:
      loop.block->set_my_def(p_def);
      break;
    case S_ALT:
    case S_INTERLEAVE:
      ags->set_my_def(p_def);
      break;
    case S_CALL:
      if (port_op.s.call.body) port_op.s.call.body->set_my_def(p_def);
      break;
    default:
      break;
    } // switch statementtype
  }

  void Statement::set_my_ags(AltGuards *p_ags)
  {
    switch (statementtype) {
    case S_BLOCK:
      block->set_my_ags(p_ags);
      break;
    case S_IF:
      if_stmt.ics->set_my_ags(p_ags);
      if (if_stmt.elseblock) if_stmt.elseblock->set_my_ags(p_ags);
      break;
    case S_SELECT:
      select.scs->set_my_ags(p_ags);
      break;
    case S_SELECTUNION:
      select_union.sus->set_my_ags(p_ags);
      break;
    case S_SELECT_CLASS:
      select_class.sccs->set_my_ags(p_ags);
      break;
    case S_FOR:
    case S_WHILE:
    case S_DOWHILE:
      loop.block->set_my_ags(p_ags);
      break;
    case S_REPEAT:
      ags = p_ags;
      break;
    default:
      break;
    } // switch statementtype
  }

  void Statement::set_my_laic_stmt(AltGuards *p_ags, Statement *p_loop_stmt)
  {
    switch (statementtype) {
    case S_BLOCK:
      block->set_my_laic_stmt(p_ags, p_loop_stmt);
      break;
    case S_IF:
      if_stmt.ics->set_my_laic_stmt(p_ags, p_loop_stmt);
      if (if_stmt.elseblock)
        if_stmt.elseblock->set_my_laic_stmt(p_ags, p_loop_stmt);
      break;
    case S_SELECT:
      select.scs->set_my_laic_stmt(p_ags, p_loop_stmt);
      break;
    case S_SELECTUNION:
      select_union.sus->set_my_laic_stmt(p_ags, p_loop_stmt);
      break;
    case S_SELECT_CLASS:
      select_class.sccs->set_my_laic_stmt(p_ags, p_loop_stmt);
      break;
    case S_ALT:
    case S_INTERLEAVE:
      if (p_loop_stmt)
        ags->set_my_laic_stmt(0, p_loop_stmt); // ags is set later
      break;
    case S_CALL:
      if (p_loop_stmt && port_op.s.call.body)
        port_op.s.call.body->set_my_laic_stmt(0, p_loop_stmt);
         // ags is set later
      break;
    case S_BREAK:
    case S_CONTINUE:
      if (p_loop_stmt)
        brk_cnt.loop_stmt=p_loop_stmt;
      brk_cnt.ags=p_ags;
      break;
    default:
      break;
    } // switch statementtype
  }

  /** \todo handle blocks, loops and conditional statements
   * (i.e. investigate their last statements within the block) */
  bool Statement::is_terminating() const
  {
    switch (statementtype) {
    case S_GOTO:
    case S_BREAK:
    case S_CONTINUE:
    case S_STOP_EXEC:
    case S_STOP_TESTCASE:
    case S_REPEAT:
    case S_RETURN:
    case S_RAISE_OOP:
      return true;
    case S_STOP_COMP:
    case S_KILL:
      // checking for self.stop, self.kill, mtc.stop and mtc.kill
      if (comp_op.compref) {
	Value *v_last = comp_op.compref->get_value_refd_last();
	if (v_last->get_valuetype() == Value::V_EXPR) {
	  switch (v_last->get_optype()) {
	  case Value::OPTYPE_COMP_SELF:
	  case Value::OPTYPE_COMP_MTC:
	    return true;
	  default:
	    break;
	  }
	}
      }
      return false;
    case S_WHILE:
    case S_DOWHILE:
      if(!loop.expr->is_unfoldable() && loop.expr->get_val_bool()) {
        return !loop.has_brk; // not endless loop if it has a break
      }
      return false;
    default:
      return false;
    }
  }

  StatementBlock::returnstatus_t Statement::has_return() const
  {
    switch (statementtype) {
    case S_BLOCK:
      return block->has_return();
    case S_IF:
      return if_stmt.ics->has_return(if_stmt.elseblock);
    case S_SELECT:
      return select.scs->has_return();
    case S_SELECTUNION:
      return select_union.sus->has_return();
    case S_SELECT_CLASS:
      return select_class.sccs->has_return();
    case S_FOR:
    case S_WHILE:
      if (loop.block->has_return() == StatementBlock::RS_NO)
	return StatementBlock::RS_NO;
      else return StatementBlock::RS_MAYBE;
    case S_DOWHILE:
      return loop.block->has_return();
    case S_ALT: {
      StatementBlock::returnstatus_t ret_val = ags->has_return();
      if (ret_val == StatementBlock::RS_YES && !ags->has_else()) {
	// the invoked defaults may skip the entire statement
	ret_val = StatementBlock::RS_MAYBE;
      }
      return ret_val; }
    case S_CALL:
      if (port_op.s.call.body) return port_op.s.call.body->has_return();
      else return StatementBlock::RS_NO;
    default:
      if (is_terminating()) return StatementBlock::RS_YES;
      else return StatementBlock::RS_NO;
    }
  }

  bool Statement::is_receiving_stmt() const
  {
    switch (statementtype) {
    case S_ALT:
    case S_INTERLEAVE:
    case S_ALTSTEP_INSTANCE:
    case S_ALTSTEP_INVOKED:
    case S_RECEIVE:
    case S_CHECK_RECEIVE:
    case S_TRIGGER:
    case S_GETCALL:
    case S_CHECK_GETCALL:
    case S_GETREPLY:
    case S_CHECK_GETREPLY:
    case S_CATCH:
    case S_CHECK_CATCH:
    case S_CHECK:
    case S_DONE:
    case S_KILLED:
    case S_TIMEOUT:
      return true;
    default:
      return false;
    } // switch statementtype
  }

  bool Statement::has_receiving_stmt() const
  {
    switch (statementtype) {
    case S_DEF:
    case S_ASSIGNMENT:
    case S_FUNCTION_INSTANCE:
    case S_FUNCTION_INVOKED:
    case S_LOG:
    case S_STRING2TTCN:
    case S_ACTION:
    case S_LABEL:
    case S_GOTO:
    case S_BREAK:
    case S_CONTINUE:
    case S_STOP_EXEC:
    case S_STOP_TESTCASE:
    case S_REPEAT:
    case S_RETURN:
    case S_ACTIVATE:
    case S_ACTIVATE_REFD:
    case S_DEACTIVATE:
    case S_SEND:
    case S_REPLY:
    case S_RAISE:
    case S_CLEAR:
    case S_START_PORT:
    case S_STOP_PORT:
    case S_HALT:
    case S_START_COMP:
    case S_START_COMP_REFD:
    case S_STOP_COMP:
    case S_KILL:
    case S_CONNECT:
    case S_DISCONNECT:
    case S_MAP:
    case S_UNMAP:
    case S_START_TIMER:
    case S_STOP_TIMER:
    case S_SETVERDICT:
    case S_TESTCASE_INSTANCE:
    case S_TESTCASE_INSTANCE_REFD:
    case S_START_PROFILER:
    case S_STOP_PROFILER:
    case S_INT2ENUM:
    case S_UPDATE:
    case S_SETSTATE:
    case S_SETENCODE:
    case S_RAISE_OOP:
      return false;
    case S_ALT:
    case S_INTERLEAVE:
    case S_ALTSTEP_INSTANCE:
    case S_ALTSTEP_INVOKED:
    case S_RECEIVE:
    case S_CHECK_RECEIVE:
    case S_TRIGGER:
    case S_GETCALL:
    case S_CHECK_GETCALL:
    case S_GETREPLY:
    case S_CHECK_GETREPLY:
    case S_CATCH:
    case S_CHECK_CATCH:
    case S_CHECK:
    case S_DONE:
    case S_KILLED:
    case S_TIMEOUT:
      return true;
    case S_BLOCK:
      return block->has_receiving_stmt();
    case S_IF:
      return if_stmt.ics->has_receiving_stmt()
        || (if_stmt.elseblock && if_stmt.elseblock->has_receiving_stmt());
    case S_SELECT:
      return select.scs->has_receiving_stmt();
    case S_SELECTUNION:
      return select_union.sus->has_receiving_stmt();
    case S_SELECT_CLASS:
      return select_class.sccs->has_receiving_stmt();
    case S_FOR:
    case S_WHILE:
    case S_DOWHILE:
      return loop.block->has_receiving_stmt();
    case S_CALL:
      return port_op.s.call.body && port_op.s.call.body->has_receiving_stmt();
    case S_ERROR:
    case S_START_UNDEF:
    case S_STOP_UNDEF:
    case S_UNKNOWN_INSTANCE:
    case S_UNKNOWN_INVOKED:
    default:
      FATAL_ERROR("Statement::has_receiving_stmt()");
    } // switch statementtype
  }

  bool Statement::can_repeat() const
  {
    switch (statementtype) {
    case S_TRIGGER:
    case S_DONE:
    case S_KILLED:
      return true;
    case S_RECEIVE:
    case S_CHECK_RECEIVE:
    case S_GETCALL:
    case S_CHECK_GETCALL:
    case S_GETREPLY:
    case S_CHECK_GETREPLY:
    case S_CATCH:
    case S_CHECK_CATCH:
    case S_CHECK:
    case S_TIMEOUT:
      return false;
    default:
      FATAL_ERROR("Statement::can_repeat()");
    } // switch statementtype
  }

  void Statement::chk()
  {
    switch (statementtype) {
    case S_ERROR:
      break;
    case S_START_UNDEF:
      chk_start_undef();
      break;
    case S_STOP_UNDEF:
      chk_stop_undef();
      break;
    case S_UNKNOWN_INSTANCE:
      chk_unknown_instance();
      break;
    case S_UNKNOWN_INVOKED:
    case S_FUNCTION_INVOKED:
    case S_ALTSTEP_INVOKED:
      chk_unknown_invoke();
      break;
    case S_DEF:
      def->chk();
      my_sb->register_def(def);
      break;
    case S_ASSIGNMENT:
      chk_assignment();
      break;
    case S_FUNCTION_INSTANCE:
      chk_function();
      break;
    case S_BLOCK:
      chk_block();
      break;
    case S_LOG:
    case S_ACTION:
    case S_STOP_TESTCASE:
      chk_log_action(logargs);
      break;
    case S_LABEL:
      // do nothing
      break;
    case S_GOTO:
      chk_goto();
      break;
    case S_IF:
      chk_if();
      break;
    case S_SELECT:
      chk_select();
      break;
    case S_SELECTUNION:
      chk_select_union();
      break;
    case S_SELECT_CLASS:
      chk_select_class();
      break;
    case S_FOR:
      chk_for();
      break;
    case S_WHILE:
      chk_while();
      break;
    case S_DOWHILE:
      chk_do_while();
      break;
    case S_BREAK:
      chk_break();
      break;
    case S_CONTINUE:
      chk_continue();
      break;
    case S_STOP_EXEC:
      // do nothing
      break;
    case S_ALT:
      chk_alt();
      break;
    case S_REPEAT:
      chk_repeat();
      break;
    case S_INTERLEAVE:
      chk_interleave();
      break;
    case S_ALTSTEP_INSTANCE:
      chk_altstep();
      break;
    case S_RETURN:
      chk_return();
      break;
    case S_ACTIVATE:
      chk_activate();
      break;
    case S_ACTIVATE_REFD:
      chk_activate_refd();
      break;
    case S_DEACTIVATE:
      chk_deactivate();
      break;
    case S_SEND:
      chk_send();
      break;
    case S_CALL:
      chk_call();
      break;
    case S_REPLY:
      chk_reply();
      break;
    case S_RAISE:
      chk_raise();
      break;
    case S_RECEIVE:
    case S_CHECK_RECEIVE:
    case S_TRIGGER:
      chk_receive();
      break;
    case S_GETCALL:
    case S_CHECK_GETCALL:
      chk_getcall();
      break;
    case S_GETREPLY:
    case S_CHECK_GETREPLY:
      chk_getreply();
      break;
    case S_CATCH:
    case S_CHECK_CATCH:
      chk_catch();
      break;
    case S_CHECK:
      chk_check();
      break;
    case S_CLEAR:
      chk_clear();
      break;
    case S_START_PORT:
    case S_STOP_PORT:
    case S_HALT:
      chk_start_stop_port();
      break;
    case S_START_COMP:
      chk_start_comp();
      break;
    case S_START_COMP_REFD:
      chk_start_comp_refd();
      break;
    case S_STOP_COMP:
    case S_KILL:
      chk_stop_kill_comp();
      break;
    case S_DONE:
      chk_done();
      break;
    case S_KILLED:
      chk_killed();
      break;
    case S_CONNECT:
    case S_DISCONNECT:
      chk_connect();
      break;
    case S_MAP:
    case S_UNMAP:
      chk_map();
      break;
    case S_START_TIMER:
      chk_start_timer();
      break;
    case S_STOP_TIMER:
      chk_stop_timer();
      break;
    case S_TIMEOUT:
      chk_timer_timeout();
      break;
    case S_SETVERDICT:
      chk_setverdict();
      chk_log_action(setverdict.logargs); // for checking verdictreason
      break;
    case S_TESTCASE_INSTANCE:
      chk_execute();
      break;
    case S_TESTCASE_INSTANCE_REFD:
      chk_execute_refd();
      break;
    case S_STRING2TTCN:
      chk_string2ttcn();
      break;
    case S_INT2ENUM:
      chk_int2enum();
      break;
    case S_START_PROFILER:
    case S_STOP_PROFILER:
      // do nothing
      break;
    case S_UPDATE:
      chk_update();
      break;
    case S_SETSTATE:
      chk_setstate();
      break;
    case S_SETENCODE:
      chk_setencode();
      break;
    case S_RAISE_OOP:
      chk_raise_oop();
      break;
    default:
      FATAL_ERROR("Statement::chk()");
    } // switch statementtype
  }

  void Statement::chk_string2ttcn()
  {
    Error_Context cntxt(this, "In string2ttcn() statement");
    convert_op.val->chk_expr_type(Type::T_CSTR, "charstring", Type::EXPECTED_DYNAMIC_VALUE);
    ///
    Common::Assignment* refd_ass = convert_op.ref->get_refd_assignment();
    if (refd_ass==NULL) {
      error("Could not determine the assignment for second parameter");
      goto error;
    }
    switch (refd_ass->get_asstype()) {
    case Definition::A_PAR_VAL_IN:
    case Definition::A_PAR_TEMPL_IN:
      refd_ass->use_as_lvalue(*convert_op.ref);
    case Definition::A_VAR:
    case Definition::A_EXCEPTION:
    case Definition::A_VAR_TEMPLATE:
    case Definition::A_PAR_VAL_OUT:
    case Definition::A_PAR_VAL_INOUT:
    case Definition::A_PAR_TEMPL_OUT:
    case Definition::A_PAR_TEMPL_INOUT: 
      // valid assignment types
      break;
    default:
      convert_op.ref->error("Reference to '%s' cannot be used as the second parameter", refd_ass->get_assname());
      goto error;
    }
    return;
  error:
    delete convert_op.val;
    delete convert_op.ref;
    statementtype = S_ERROR;
  }
  
  void Statement::chk_int2enum()
  {
    Error_Context cntxt(this, "In int2enum() statement");
    convert_op.val->chk_expr_type(Type::T_INT, "integer", Type::EXPECTED_DYNAMIC_VALUE);
    ///
    Common::Assignment* refd_ass = convert_op.ref->get_refd_assignment();
    if (refd_ass==NULL) {
      error("Could not determine the assignment for second parameter");
      goto error;
    }
    {
      Type* t = convert_op.ref->chk_variable_ref()->get_type_refd_last();
      Type::typetype_t tt = t->get_typetype_ttcn3();
      if (Type::T_ENUM_T != tt) {
        if (Type::T_CHOICE_T == tt) {
          CompField* def_alt = t->get_default_alternative();
          if (def_alt != NULL) {
            Error_Context cntxt2(convert_op.ref, "Using default alternative `%s' in value of union type `%s'",
              def_alt->get_name().get_dispname().c_str(), t->get_typename().c_str());
            convert_op.ref->use_default_alternative(def_alt->get_name());
            chk_int2enum();
            return;
          }
        }
        convert_op.ref->error("A reference to variable or value parameter of "
          "type enumerated was expected");
        goto error;
      }
    }
    return;
  error:
    delete convert_op.val;
    delete convert_op.ref;
    statementtype = S_ERROR;
  }
  
  void Statement::chk_allowed_interleave()
  {
    switch (statementtype) {
    case S_BLOCK:
      block->chk_allowed_interleave();
      break;
    case S_LABEL:
      error("Label statement is not allowed within an interleave statement");
      break;
    case S_GOTO:
      error("Goto statement is not allowed within an interleave statement");
      break;
    case S_IF:
      if_stmt.ics->chk_allowed_interleave();
      if (if_stmt.elseblock) if_stmt.elseblock->chk_allowed_interleave();
      break;
    case S_SELECT:
      select.scs->chk_allowed_interleave();
      break;
    case S_SELECTUNION:
      select_union.sus->chk_allowed_interleave();
      break;
    case S_SELECT_CLASS:
      select_class.sccs->chk_allowed_interleave();
      break;
    case S_FOR:
    case S_WHILE:
    case S_DOWHILE:
      loop.block->chk_allowed_interleave();
      break;
    case S_ALT:
      ags->chk_allowed_interleave();
      break;
    case S_REPEAT:
      error("Repeat statement is not allowed within an interleave statement");
      break;
    case S_ALTSTEP_INSTANCE:
      error("Invocation of an altstep is not allowed within an interleave "
	"statement");
      break;
    case S_ALTSTEP_INVOKED:
      error("Invocation of an altstep type is not allowed within an interleave"
        "statement");
      break;
    case S_RETURN:
      error("Return statement is not allowed within an interleave statement");
      break;
    case S_CALL:
      if (port_op.s.call.body) port_op.s.call.body->chk_allowed_interleave();
      break;
    case S_RAISE_OOP:
      // TODO: is this allowed in an interleave? (if 'return' isn't allowed, then this probably shouldn't be either)
      error("Raise statement is not allowed within an interleave statement");
      break;
    default:
      // the other statements are allowed
      break;
    }
  }

  void Statement::chk_start_undef()
  {
    Reference *t_ref = undefstartstop.ref;
    Value *t_val = undefstartstop.val;
    Common::Assignment *t_ass;
    {
      Error_Context cntxt(this, "In start statement");
      t_ass = t_ref->get_refd_assignment();
    }
    if (!t_ass) goto error;
    switch (t_ass->get_asstype()) {
    case Definition::A_PORT:
    case Definition::A_PAR_PORT:
      statementtype = S_START_PORT;
      port_op.portref = dynamic_cast<Reference*>(t_ref);
      if (!port_op.portref) goto error;
      if (t_val) {
	t_val->error("Start port operation cannot have argument");
	delete t_val;
      }
      chk_start_stop_port();
      break;
    case Definition::A_TIMER:
    case Definition::A_PAR_TIMER:
      statementtype = S_START_TIMER;
      timer_op.timerref = dynamic_cast<Reference*>(t_ref);
      if (!timer_op.timerref) goto error;
      timer_op.value = t_val;
      chk_start_timer();
      break;
    case Definition::A_CONST:
    case Definition::A_EXT_CONST:
    case Definition::A_MODULEPAR:
    case Definition::A_VAR:
    case Definition::A_FUNCTION_RVAL:
    case Definition::A_EXT_FUNCTION_RVAL:
    case Definition::A_PAR_VAL_IN:
    case Definition::A_PAR_VAL_OUT:
    case Definition::A_PAR_VAL_INOUT:
      statementtype = S_START_COMP;
      if (!t_val) {
        error("The argument of start operation is missing, although it cannot "
	  "be a start timer or start port operation");
	goto error;
      } else if (t_val->get_valuetype() != Value::V_REFD) {
        t_val->error("The argument of start operation is not a function, "
	  "although it cannot be a start timer or start port operation");
	goto error;
      } else {
        comp_op.funcinstref = t_val->steal_ttcn_ref();
	delete t_val;
      }
      comp_op.compref = new Value(Value::V_REFD, t_ref);
      comp_op.compref->set_my_scope(t_ref->get_my_scope());
      comp_op.compref->set_fullname(t_ref->get_fullname());
      comp_op.compref->set_location(*t_ref);
      chk_start_comp();
      break;
    default:
      t_ref->error("Port, timer or component reference was expected as the "
	"operand of start operation instead of %s",
	t_ass->get_description().c_str());
      goto error;
    }
    return;
  error:
    delete t_ref;
    delete t_val;
    statementtype = S_ERROR;
  }

  void Statement::chk_stop_undef()
  {
    Reference *t_ref = undefstartstop.ref;
    Common::Assignment *t_ass;
    {
      Error_Context cntxt(this, "In stop statement");
      t_ass = t_ref->get_refd_assignment();
    }
    if (!t_ass) goto error;
    // Determine what it is that we are trying to stop; change statementtype
    switch (t_ass->get_asstype()) {
    case Definition::A_PORT:
    case Definition::A_PAR_PORT:
      statementtype = S_STOP_PORT;
      port_op.portref = dynamic_cast<Reference*>(t_ref);
      if (!port_op.portref) goto error;
      chk_start_stop_port();
      break;
    case Definition::A_TIMER:
    case Definition::A_PAR_TIMER:
      statementtype = S_STOP_TIMER;
      timer_op.timerref = dynamic_cast<Reference*>(t_ref);
      if (!timer_op.timerref) goto error;
      timer_op.value = 0;
      chk_stop_timer();
      break;
    case Definition::A_CONST:
    case Definition::A_EXT_CONST:
    case Definition::A_MODULEPAR:
    case Definition::A_VAR:
    case Definition::A_FUNCTION_RVAL:
    case Definition::A_EXT_FUNCTION_RVAL:
    case Definition::A_PAR_VAL_IN:
    case Definition::A_PAR_VAL_OUT:
    case Definition::A_PAR_VAL_INOUT:
      statementtype = S_STOP_COMP;
      comp_op.compref = new Value(Value::V_REFD, t_ref);
      comp_op.compref->set_my_scope(t_ref->get_my_scope());
      comp_op.compref->set_fullname(t_ref->get_fullname());
      comp_op.compref->set_location(*t_ref);
      chk_stop_kill_comp();
      break;
    default:
      t_ref->error("Port, timer or component reference was expected as the "
	"operand of stop operation instead of %s",
	t_ass->get_description().c_str());
      goto error;
    }
    return;
  error:
    delete t_ref;
    statementtype = S_ERROR;
  }

  void Statement::chk_unknown_instance()
  {
    Common::Assignment *t_ass;
    {
      Error_Context cntxt(this, "In function or altstep instance");
      t_ass = ref_pard->get_refd_assignment(false);
    }
    if (!t_ass) goto error;
    switch (t_ass->get_asstype()) {
    case Common::Assignment::A_VAR:
    case Common::Assignment::A_EXCEPTION:
    case Common::Assignment::A_PAR_VAL:
    case Common::Assignment::A_PAR_VAL_IN:
    case Common::Assignment::A_PAR_VAL_INOUT:
    case Common::Assignment::A_PAR_VAL_OUT:
      if (t_ass->get_Type()->get_type_refd_last()->get_typetype() != Common::Type::T_CLASS) {
        ref_pard->error("Reference to a function or altstep was expected "
          "instead of %s, which cannot be invoked",
          t_ass->get_description().c_str());
        goto error;
      }
      // else fall through
    case Common::Assignment::A_FUNCTION:
    case Common::Assignment::A_FUNCTION_RVAL:
    case Common::Assignment::A_FUNCTION_RTEMP:
    case Common::Assignment::A_EXT_FUNCTION:
    case Common::Assignment::A_EXT_FUNCTION_RVAL:
    case Common::Assignment::A_EXT_FUNCTION_RTEMP:
      statementtype = S_FUNCTION_INSTANCE;
      chk_function();
      break;
    case Common::Assignment::A_ALTSTEP:
      statementtype = S_ALTSTEP_INSTANCE;
      chk_altstep();
      break;
    default:
      ref_pard->error("Reference to a function or altstep was expected "
	"instead of %s, which cannot be invoked",
	t_ass->get_description().c_str());
      goto error;
    }
    return;
  error:
    clean_up();
    statementtype = S_ERROR;
  }

  void Statement::chk_unknown_invoke()
  {
    Error_Context cntxt(this, "In apply operation");
    Type *t = fau_refd.value->get_expr_governor_last();
    if (!t) goto error;
    switch (t->get_typetype()) {
    case Type::T_ERROR:
      goto error;
    case Type::T_FUNCTION:
      statementtype = S_FUNCTION_INVOKED;
      if (t->get_function_return_type()) warning("The value returned by "
	"function type `%s' is not used", t->get_typename().c_str());
      break;
    case Type::T_ALTSTEP:
      statementtype = S_ALTSTEP_INVOKED;
      break;
    default:
      fau_refd.value->error("A value of type function or altstep was "
	"expected instead of `%s'", t->get_typename().c_str());
      goto error;
    }
    my_sb->chk_runs_on_clause(t, *this, "call");
    {
      ActualParList *parlist = new Ttcn::ActualParList;
      Ttcn::FormalParList *fp_list = t->get_fat_parameters();
      bool is_erroneous = fp_list->fold_named_and_chk(fau_refd.t_list1,
	parlist);
      delete fau_refd.t_list1;
      if(is_erroneous) {
        delete parlist;
        fau_refd.ap_list2 = 0;
      } else {
	parlist->set_fullname(get_fullname());
	parlist->set_my_scope(my_sb);
        fau_refd.ap_list2 = parlist;
      }
    }
    return;
error:
    clean_up();
    statementtype = S_ERROR;
  }

  void Statement::chk_assignment()
  {
    Error_Context cntxt(this, "In variable assignment");
    Definition* sb_def = my_sb->get_my_def();
    if (sb_def != NULL && sb_def->get_asstype() == Common::Assignment::A_CONSTRUCTOR) {
      ass->set_in_contructor();
    }
    ass->chk();
  }

  void Statement::chk_function()
  {
    Error_Context cntxt(this, "In function instance");
    Common::Assignment *t_ass = ref_pard->get_refd_assignment();
    switch (t_ass->get_asstype()) {
    case Common::Assignment::A_VAR:
    case Common::Assignment::A_EXCEPTION:
    case Common::Assignment::A_PAR_VAL:
    case Common::Assignment::A_PAR_VAL_IN:
    case Common::Assignment::A_PAR_VAL_INOUT:
    case Common::Assignment::A_PAR_VAL_OUT: {
      // it could be a class object method
      Common::Assignment* last_method = NULL;
      Common::Type* end_type = t_ass->get_Type()->get_field_type(ref_pard->get_subrefs(),
        Type::EXPECTED_DYNAMIC_VALUE, 0, false, &last_method);
      if (end_type == NULL && last_method == NULL) {
        // invalid subreferences (the error has already been reported)
        return;
      }
      if (last_method == NULL) {
        ref_pard->error("Reference to a function or altstep was expected");
      }
      else {
        // do the checks on the method at the end of the subreferences instead
        t_ass = last_method;
      }
      break; }
    default:
      break;
    }
    if (t_ass->get_PortType()) {
      ref_pard->error("Function with `port' clause cannot be called directly.");
    }
    my_sb->chk_runs_on_clause(t_ass, *ref_pard, "call");
    
    bool in_control_part = my_sb->get_my_def() == NULL;
    my_sb->chk_mtc_clause(t_ass, *ref_pard, "call", in_control_part);
    my_sb->chk_system_clause(t_ass, *ref_pard, "call", in_control_part);
    
    if (t_ass->get_Type())
      ref_pard->warning("The value returned by %s is not used",
	      t_ass->get_description().c_str());
  }

  void Statement::chk_block()
  {
    Error_Context cntxt(this, "In statement block");
    block->chk();
  }

  void Statement::chk_log_action(LogArguments *lga)
  {
    Error_Context cntxt(this, "In %s statement", get_stmt_name());
    if (lga) {
      lga->chk();
      if (!semantic_check_only) lga->join_strings();
    }
  }

  void Statement::chk_goto()
  {
    Error_Context cntxt(this, "In goto statement");
    if (!my_sb->has_label(*go_to.id)) {
      error("Label `%s' is used, but not defined",
	go_to.id->get_dispname().c_str());
      go_to.label = 0;
      go_to.jumps_forward = false;
      return;
    }
    Statement *label_stmt = my_sb->get_label(*go_to.id);
    label_stmt->label.used = true;
    StatementBlock *label_sb = label_stmt->get_my_sb();
    // the index of the label in its own statement block
    size_t label_idx = label_stmt->get_my_sb_index();
    // the index of the goto statement (or its parent statement) in the
    // statement block of the label
    size_t goto_idx;
    if (my_sb == label_sb) goto_idx = go_to.stmt_idx;
    else {
      // the goto statement is within a nested statement block
      StatementBlock *goto_sb = my_sb, *parent_sb = my_sb->get_my_sb();
      while (parent_sb != label_sb) {
	// go up until the block of the label is found
	if (!parent_sb) FATAL_ERROR("Statement::chk_goto()");
	goto_sb = parent_sb;
	parent_sb = parent_sb->get_my_sb();
      }
      goto_idx = goto_sb->get_my_sb_index();
    }
    if (label_idx > goto_idx) {
      bool error_flag = false;
      for (size_t i = goto_idx + 1; i < label_idx; i++) {
	Statement *stmt = label_sb->get_stmt_byIndex(i);
	if (stmt->get_statementtype() != S_DEF) continue;
	if (!error_flag) {
	  error("Jump to label `%s' crosses local definition",
	    go_to.id->get_dispname().c_str());
	  error_flag = true;
	}
	stmt->note("Definition of %s is here",
	  stmt->get_def()->get_description().c_str());
      }
      if (error_flag)
	label_stmt->note("Label `%s' is here",
	  go_to.id->get_dispname().c_str());
      go_to.jumps_forward = true;
    } else go_to.jumps_forward = false;
    go_to.label = label_stmt;
  }

  void Statement::chk_if()
  {
    bool unreach=false;
    if_stmt.ics->chk(unreach);
    if(if_stmt.elseblock) {
      Error_Context cntxt(if_stmt.elseblock_location, "In else statement");
      if(unreach) if_stmt.elseblock_location->warning
        ("Control never reaches this code because of previous effective"
         " condition(s)");
      if_stmt.elseblock->chk();
    }
  }

  /** \todo review */
  void Statement::chk_select()
  {
    Error_Context cntxt(this, "In select case statement");
    Type *t_gov=0;
    for(int turn=0; turn<2; turn++) {
      if(turn) select.expr->set_lowerid_to_ref();
      Type::typetype_t tt=select.expr->get_expr_returntype();
      t_gov=select.expr->get_expr_governor(Type::EXPECTED_DYNAMIC_VALUE);
      if(!t_gov || tt==Type::T_ERROR) {
        SelectCases *scs=select.scs;
        for(size_t i=0; i<scs->get_nof_scs(); i++) {
          TemplateInstances *tis=scs->get_sc_byIndex(i)->get_tis();
          if(!tis) continue;
          for(size_t j=0; j<tis->get_nof_tis(); j++) {
            TemplateInstance *ti=tis->get_ti_byIndex(j);
            if(turn) ti->get_Template()->set_lowerid_to_ref();
            t_gov=ti->get_expr_governor(Type::EXPECTED_TEMPLATE);
            tt=ti->get_expr_returntype(Type::EXPECTED_TEMPLATE);
            if(t_gov && tt!=Type::T_ERROR) break;
          } // for j
          if(t_gov) break;
        } // for i
      }
      else t_gov=select.expr->get_expr_governor(Type::EXPECTED_DYNAMIC_VALUE);
    } // for turn
    if(!t_gov) {
      select.expr->error("Cannot determine the type of the expression");
      t_gov=Type::get_pooltype(Type::T_ERROR);
    }
    select.expr->set_my_governor(t_gov);
    t_gov->chk_this_value_ref(select.expr);
    t_gov->chk_this_value(select.expr, 0, Type::EXPECTED_DYNAMIC_VALUE,
      INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK);
    select.scs->chk(t_gov);
  }
  
  void Statement::chk_select_union()
  {
    Error_Context cntxt(this, "In select union statement");
    select_union.expr->set_lowerid_to_ref();
    Type *t_gov=select_union.expr->get_expr_governor_last();
    boolean error = FALSE;
    if (t_gov == NULL || t_gov->get_typetype() == Type::T_ERROR) {
      select.expr->error("Cannot determine the type of the head expression");
      error = TRUE;
    }
    
    if (error == FALSE) {
      Type::typetype_t tt = t_gov->get_typetype();
      if (tt != Type::T_CHOICE_T && tt != Type::T_CHOICE_A && tt != Type::T_ANYTYPE) {
        select.expr->error("The head must be of a union type or anytype");
        error = TRUE;
      }
    }
    
    if (error == FALSE) {
      for(size_t i=0; i<select_union.sus->get_nof_sus(); i++) {
        size_t size = select_union.sus->get_su_byIndex(i)->get_ids().size();
        for(size_t j=0; j<size; j++) {
          const Identifier *id = select_union.sus->get_su_byIndex(i)->get_id_byIndex(j);
          if (!t_gov->has_comp_withName(*id)) {
            select_union.sus->get_su_byIndex(i)->error("In the %lu. branch: '%s' is not an alternative of union type '%s'", i+1, id->get_ttcnname().c_str(), t_gov->get_typename().c_str());
            continue;
          }
          for(size_t i2=i; i2<select_union.sus->get_nof_sus(); i2++) {
            size_t size2 = select_union.sus->get_su_byIndex(i2)->get_ids().size();
            for(size_t j2=j; j2<size2; j2++) {
              if (i == i2 && j == j2) continue;
              const Identifier *id2 = select_union.sus->get_su_byIndex(i2)->get_id_byIndex(j2);
              if (id->get_ttcnname() == id2->get_ttcnname()) {
                select_union.sus->get_su_byIndex(i2)->error("The '%s' is already present in the %lu. branch of select union", id->get_ttcnname().c_str(), i+1);
              }
            }
          }
        }
      }
    }
    
    select_union.sus->chk();
  }
  
  void Statement::chk_select_class()
  {
    Error_Context cntxt(this, "In select class statement");
    Type* ref_type = select_class.ref->chk_variable_ref();
    ClassTypeBody* ref_class = NULL;
    if (ref_type != NULL) {
      Type* ref_type_last = ref_type->get_type_refd_last();
      if (ref_type_last->get_typetype() != Type::T_CLASS) {
        select_class.ref->error("Reference to a class object was expected");
      }
      else {
        ref_class = ref_type_last->get_class_type_body();
      }
    }
    select_class.sccs->chk(ref_class);
  }
  
  void Statement::chk_for()
  {
    Error_Context cntxt(this, "In for statement");
    if (loop.for_stmt.varinst) loop.for_stmt.init_varinst->chk_for();
    else loop.for_stmt.init_ass->chk();
    loop.for_stmt.finalexpr->chk_expr_bool(Type::EXPECTED_DYNAMIC_VALUE);
    if(!loop.for_stmt.finalexpr->is_unfoldable()
       && !loop.for_stmt.finalexpr->get_val_bool())
      loop.for_stmt.finalexpr->warning
        ("Control never reaches this code because the"
         " final conditional expression evals to false");
    loop.for_stmt.step->chk();
    loop.block->set_my_laic_stmt(0, this);
    loop.block->chk();
  }

  void Statement::chk_while()
  {
    Error_Context cntxt(this, "In while statement");
    loop.expr->chk_expr_bool(Type::EXPECTED_DYNAMIC_VALUE);
    if(!loop.expr->is_unfoldable() && !loop.expr->get_val_bool())
      loop.expr->warning("Control never reaches this code because the"
                         " conditional expression evals to false");
    loop.block->set_my_laic_stmt(0, this);
    loop.block->chk();
  }

  void Statement::chk_do_while()
  {
    Error_Context cntxt(this, "In do-while statement");
    loop.block->set_my_laic_stmt(0, this);
    loop.block->chk();
    loop.expr->chk_expr_bool(Type::EXPECTED_DYNAMIC_VALUE);
  }

  void Statement::chk_break()
  {
    Error_Context cntxt(this, "In break statement");
    if (!brk_cnt.loop_stmt && !brk_cnt.ags)
      error("Break statement cannot be used outside loops, alt or interleave"
        " statements, altsteps or response and exception handling part of call"
        " operations");
    if (brk_cnt.loop_stmt)
      brk_cnt.loop_stmt->loop.has_brk=true;
  }

  void Statement::chk_continue()
  {
    Error_Context cntxt(this, "In continue statement");
    if (brk_cnt.loop_stmt) {
      brk_cnt.loop_stmt->loop.has_cnt=true;
      if (brk_cnt.ags) brk_cnt.loop_stmt->loop.has_cnt_in_ags=true;
    } else
      error("Continue statement cannot be used outside loops");
  }

  void Statement::chk_alt()
  {
    Error_Context cntxt(this, "In alt construct");
    ags->set_my_ags(ags);
    ags->set_my_laic_stmt(ags, 0);
    ags->chk();
  }

  void Statement::chk_repeat()
  {
    Error_Context cntxt(this, "In repeat statement");
    if (ags) ags->repeat_found();
    else error("Repeat statement cannot be used outside alt statements, "
      "altsteps or response and exception handling part of call operations");
  }

  void Statement::chk_interleave()
  {
    Error_Context cntxt(this, "In interleave statement");
    ags->set_my_laic_stmt(ags, 0);
    ags->chk();
    ags->chk_allowed_interleave();
  }

  void Statement::chk_altstep()
  {
    Error_Context cntxt(this, "In altstep instance");
    Common::Assignment *t_ass = ref_pard->get_refd_assignment();
    my_sb->chk_runs_on_clause(t_ass, *ref_pard, "call");
    bool in_control_part = my_sb->get_my_def() == NULL;
    my_sb->chk_mtc_clause(t_ass, *ref_pard, "call", in_control_part);
    my_sb->chk_system_clause(t_ass, *ref_pard, "call", in_control_part);
  }

  void Statement::chk_return()
  {
    Error_Context cntxt(this, "In return statement");
    Definition *my_def = my_sb->get_my_def();
    if (!my_def) {
      if (my_sb->is_in_dynamic_template()) {
        if (returnexpr.t == NULL) {
          error("Missing return value. The dynamic template's statement block "
            "should return a boolean value");
          goto error;
        }
        else if (!returnexpr.t->is_Value()) {
          returnexpr.t->error("A specific value without matching symbols was "
            "expected as return value");
          goto error;
        }
        else {
          returnexpr.v = returnexpr.t->get_Value();
          delete returnexpr.t;
          returnexpr.t = 0;
          Type* return_type = Type::get_pooltype(Type::T_BOOL);
          returnexpr.v->set_my_governor(return_type);
          return_type->chk_this_value_ref(returnexpr.v);
          return_type->chk_this_value(returnexpr.v, 0, Type::EXPECTED_DYNAMIC_VALUE,
            INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK);
          return;
        }
      }
      else { // control part
        error("Return statement cannot be used in the control part. "
              "It is allowed only in functions and altsteps");
        goto error;
      }
    }
    if (my_sb->is_in_finally_block()) {
      error("Return statement cannot be used inside a finally block or the destructor of a class.");
      goto error;
    }
    switch (my_def->get_asstype()) {
    case Definition::A_FUNCTION:
      if (returnexpr.t) {
        returnexpr.t->error("Unexpected return value. The function does not "
                            "have return type");
	goto error;
      }
      break;
    case Definition::A_FUNCTION_RVAL:
      if (!returnexpr.t) {
	error("Missing return value. The function should return a value of "
	  "type `%s'", my_def->get_Type()->get_typename().c_str());
	goto error;
      } else if (!returnexpr.t->is_Value()) {
	returnexpr.t->error("A specific value without matching symbols was "
                            "expected as return value");
	goto error;
      } else {
	returnexpr.v = returnexpr.t->get_Value();
	delete returnexpr.t;
	returnexpr.t = 0;
	Type *return_type = my_def->get_Type();
	returnexpr.v->set_my_governor(return_type);
	return_type->chk_this_value_ref(returnexpr.v);
	return_type->chk_this_value(returnexpr.v, 0, Type::EXPECTED_DYNAMIC_VALUE,
	  INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK);
      }
      break;
    case Definition::A_FUNCTION_RTEMP:
      if (!returnexpr.t) {
	error("Missing return template. The function should return a template "
	  "of type `%s'", my_def->get_Type()->get_typename().c_str());
	goto error;
      } else {
	Type *return_type = my_def->get_Type();
	returnexpr.t->set_my_governor(return_type);
	return_type->chk_this_template_ref(returnexpr.t);
	return_type->chk_this_template_generic(returnexpr.t, INCOMPLETE_NOT_ALLOWED,
	  OMIT_ALLOWED, ANY_OR_OMIT_ALLOWED, SUB_CHK, NOT_IMPLICIT_OMIT, NOT_CLASS_MEMBER_INIT, 0);
        Def_Function_Base* dfb = dynamic_cast<Def_Function_Base*>(my_def);
        if (!dfb) FATAL_ERROR("Statement::chk_return()");
        returnexpr.gen_restriction_check =
          returnexpr.t->chk_restriction("return template",
            dfb->get_template_restriction(), this);
      }
      break;
    case Definition::A_ALTSTEP:
      if (returnexpr.t) {
	returnexpr.t->error("An altstep cannot return a value");
	goto error;
      }
      break;
    default:
      error("Return statement cannot be used in a %s. It is allowed only in "
            "functions and altsteps", my_def->get_assname());
      goto error;
    }
    return;
  error:
    delete returnexpr.t;
    returnexpr.t = 0;
  }

  void Statement::chk_activate()
  {
    Error_Context cntxt(this, "In activate statement");
    if (!ref_pard->chk_activate_argument()) {
      clean_up();
      statementtype = S_ERROR;
    }
  }

  void Statement::chk_activate_refd()
  {
    Error_Context cntxt(this, "In activate statement");
    Type *t = fau_refd.value->get_expr_governor_last();
    if (!t) goto error;
    switch (t->get_typetype()) {
    case Type::T_ERROR:
      goto error;
    case Type::T_ALTSTEP:
      break;
    default:
      fau_refd.value->error("A value of type altstep was expected in the "
	"argument of `derefers()' instead of `%s'", t->get_typename().c_str());
      goto error;
    }
    if (t->get_fat_runs_on_self()) {
      fau_refd.value->error("The argument of `derefers()' cannot be an altstep "
        "reference with 'runs on self' clause");
      goto error;
    }
    my_sb->chk_runs_on_clause(t, *this, "activate");
    {
      ActualParList *parlist = new ActualParList;
      Ttcn::FormalParList *fp_list = t->get_fat_parameters();
      bool is_erroneous = fp_list->fold_named_and_chk(fau_refd.t_list1,
	parlist);
      delete fau_refd.t_list1;
      if(is_erroneous) {
        delete parlist;
        fau_refd.ap_list2 = 0;
        goto error;
      } else {
        parlist->set_fullname(get_fullname());
	parlist->set_my_scope(my_sb);
        fau_refd.ap_list2 = parlist;
        if (!fp_list->chk_activate_argument(parlist,get_fullname().c_str()))
          goto error;
      }
    }
    return;
error:
    clean_up();
    statementtype = S_ERROR;
  }

  void Statement::chk_deactivate()
  {
    Error_Context cntxt(this, "In deactivate statement");
    if (!use_runtime_2 &&
        (my_sb->get_my_def()->get_asstype() == Common::Assignment::A_ALTSTEP ||
        my_sb->get_my_def()->get_asstype() == Common::Assignment::A_FUNCTION)) {
      // The automatic shadowing of 'in' parameters in altsteps is not done in RT1
      // for performance reasons. Issue a warning about their possible deletion.
      warning("Calling `deactivate()' in a function or altstep. This %s "
        "delete the `in' parameters of %s currently running altstep%s.",
        deactivate != NULL ? "might" : "will",
        deactivate != NULL ? "a" : "all", deactivate != NULL ? "" : "s");
    }
    if (deactivate) {
      deactivate->chk_expr_default(Type::EXPECTED_DYNAMIC_VALUE);
    }
  }

  void Statement::chk_send()
  {
    Error_Context cntxt(this, "In send statement");
    // checking the port reference
    Type *port_type = NULL;
    if (port_op.translate) {
      PortScope* ps = my_sb->get_scope_port();
      if (ps) {
        port_type = ps->get_port_type();
      } else {
        error("Cannot determine the type of the port: Missing port clause on the function.");
      }
    } else {
      port_type = chk_port_ref(port_op.portref);
    }
    // determining the message type
    Type *msg_type = 0;
    bool msg_type_determined = false;
    if (port_type) {
      Ttcn::PortTypeBody *port_type_body = port_type->get_PortBody();
      TypeSet *out_msgs = port_type_body->get_out_msgs();
      if (out_msgs) {
        if (out_msgs->get_nof_types() == 1) {
	  // there is only one outgoing message type
	  msg_type = out_msgs->get_type_byIndex(0);
	} else {
	  // there are more than one outgoing message types
	  msg_type = get_msg_sig_type(port_op.s.sendpar);
	  if (msg_type) {
	    size_t nof_comp_types =
	      out_msgs->get_nof_compatible_types(msg_type);
	    if (nof_comp_types == 0) {
	      port_op.s.sendpar->error("Message type `%s' is not present on "
		"the outgoing list of port type `%s'",
		msg_type->get_typename().c_str(),
		port_type->get_typename().c_str());
	    } else if (nof_comp_types > 1) {
	      port_op.s.sendpar->error("Type of the message is ambiguous: "
		"`%s' is compatible with more than one outgoing message types "
		"of port type `%s'", msg_type->get_typename().c_str(),
		port_type->get_typename().c_str());
	    }
	  } else {
	    port_op.s.sendpar->error("Cannot determine the type of the "
	      "outgoing message");
	  }
	}
	msg_type_determined = true;
      } else if (port_type_body->get_operation_mode() ==
		 PortTypeBody::PO_PROCEDURE) {
        port_op.portref->error("Message-based operation `send' is not "
	  "applicable to a procedure-based port of type `%s'",
	  port_type->get_typename().c_str());
      } else {
        port_op.portref->error("Port type `%s' does not have any outgoing "
	  "message types", port_type->get_typename().c_str());
      }
    }
    // determining the message type if it is not done so far
    if (!msg_type_determined) {
      msg_type = get_msg_sig_type(port_op.s.sendpar);
    }
    if (!msg_type) msg_type = Type::get_pooltype(Type::T_ERROR);
    // checking the parameter (template instance)
    port_op.s.sendpar->chk(msg_type);
    // checking for invalid message types
    msg_type = msg_type->get_type_refd_last();
    switch (msg_type->get_typetype()) {
    case Type::T_SIGNATURE:
      port_op.s.sendpar->error("The type of send parameter is signature `%s', "
	"which cannot be a message type", msg_type->get_typename().c_str());
      break;
    case Type::T_PORT:
      port_op.s.sendpar->error("The type of send parameter is port type `%s', "
	"which cannot be a message type", msg_type->get_typename().c_str());
      break;
    case Type::T_DEFAULT:
      port_op.s.sendpar->error("The type of send parameter is the `default' "
	"type, which cannot be a message type");
    default:
      break;
    }
    // checking for presence of wildcards in the template body
    port_op.s.sendpar->get_Template()->chk_specific_value(false);
    // checking to clause
    chk_to_clause(port_type);
    // checking timestamp redirect
    chk_timestamp_redirect(port_type, true);
  }

  void Statement::chk_call()
  {
    Error_Context cntxt(this, "In call statement");
    // checking the port reference
    Type *port_type = chk_port_ref(port_op.portref);
    // determining the signature of the argument
    Type *signature = 0;
    bool signature_determined = false;
    if (port_type) {
      PortTypeBody *port_type_body = port_type->get_PortBody();
      TypeSet *out_sigs = port_type_body->get_out_sigs();
      if (out_sigs) {
        if (out_sigs->get_nof_types() == 1) {
	  // there is only one outgoing signature
	  signature = out_sigs->get_type_byIndex(0);
	} else {
	  // there are more than one outgoing signatures
	  signature = get_msg_sig_type(port_op.s.sendpar);
	  if (signature) {
	    if (!out_sigs->has_type(signature)) {
	      port_op.s.sendpar->error("Signature `%s' is not present on the "
		"outgoing list of port type `%s'",
		signature->get_typename().c_str(),
		port_type->get_typename().c_str());
	    }
	  } else {
	    port_op.s.sendpar->error("Cannot determine the type of the "
	      "signature");
	  }
	}
	signature_determined = true;
      } else if (port_type_body->get_operation_mode() ==
		 PortTypeBody::PO_MESSAGE) {
        port_op.portref->error("Procedure-based operation `call' is not "
	  "applicable to a message-based port of type `%s'",
	  port_type->get_typename().c_str());
      } else {
        port_op.portref->error("Port type `%s' does not have any outgoing "
	  "signatures", port_type->get_typename().c_str());
      }
    }
    if (!signature_determined)
      signature = get_msg_sig_type(port_op.s.sendpar);
    if (!signature) signature = Type::get_pooltype(Type::T_ERROR);
    // checking the parameter (template instance)
    port_op.s.sendpar->chk(signature);
    signature = signature->get_type_refd_last();
    bool is_noblock_sig = false;
    Type::typetype_t tt = signature->get_typetype();
    switch (tt) {
    case Type::T_SIGNATURE:
      // the signature is known and correct
      is_noblock_sig = signature->is_nonblocking_signature();
    case Type::T_ERROR:
      break;
    default:
	port_op.s.sendpar->error("The type of parameter is `%s', which is not "
	  "a signature", signature->get_typename().c_str());
    }
    // checking presence/absence of optional parts
    if (is_noblock_sig) {
      if (port_op.s.call.timer) {
	port_op.s.call.timer->error("A call of non-blocking signature `%s' "
	  "cannot have call timer", signature->get_typename().c_str());
      } else if (port_op.s.call.nowait) {
	error("A call of non-blocking signature `%s' cannot use the "
	  "`nowait' keyword", signature->get_typename().c_str());
      }
      if (port_op.s.call.body) {
	error("A call of non-blocking signature `%s' cannot have "
	  "response and exception handling part",
	  signature->get_typename().c_str());
      }
    } else if (port_op.s.call.nowait) {
      if (port_op.s.call.body) {
	error("A call with `nowait' keyword cannot have response and "
	  "exception handling part");
      }
    } else {
      // do not issue any error if the signature is erroneous
      // because it could have been a non-blocking one
      if (tt == Type::T_SIGNATURE && !port_op.s.call.body) {
	error("Response and exception handling part is missing from "
	  "blocking call operation");
      }
    }
    // checking call timer
    if (port_op.s.call.timer) {
      Error_Context cntxt2(port_op.s.call.timer, "In call timer value");
      port_op.s.call.timer->chk_expr_float(Type::EXPECTED_DYNAMIC_VALUE);
      Value *t_val = port_op.s.call.timer->get_value_refd_last();
      if (t_val->get_valuetype() == Value::V_REAL) {
        ttcn3float v_real = t_val->get_val_Real();
        if (v_real < 0.0) {
          port_op.s.call.timer->error("The call timer has "
            "negative duration: `%s'", Real2string(v_real).c_str());
        } else if (isSpecialFloatValue(v_real)) {
          port_op.s.call.timer->error("The call timer duration cannot be %s",
                                      Real2string(v_real).c_str());
        }
      }
    }
    // checking to clause
    chk_to_clause(port_type);
    // checking timestamp redirect
    chk_timestamp_redirect(port_type, true);
    // checking response and exception handling part
    if (port_op.s.call.body) chk_call_body(port_type, signature);
  }

  void Statement::chk_reply()
  {
    Error_Context cntxt(this, "In reply statement");
    // checking the port reference
    Type *port_type = chk_port_ref(port_op.portref);
    // determining the signature of the argument
    Type *signature = 0;
    bool signature_determined = false;
    if (port_type) {
      PortTypeBody *port_type_body = port_type->get_PortBody();
      TypeSet *in_sigs = port_type_body->get_in_sigs();
      if (in_sigs) {
        if (in_sigs->get_nof_types() == 1) {
	  // there is only one incoming signature
	  signature = in_sigs->get_type_byIndex(0);
	} else {
	  // there are more than one incoming signatures
	  signature = get_msg_sig_type(port_op.s.sendpar);
	  if (signature) {
	    if (!in_sigs->has_type(signature)) {
	      port_op.s.sendpar->error("Signature `%s' is not present on the "
		"incoming list of port type `%s'",
		signature->get_typename().c_str(),
		port_type->get_typename().c_str());
	    }
	  } else {
	    port_op.s.sendpar->error("Cannot determine the type of the "
	      "signature");
	  }
	}
	signature_determined = true;
      } else if (port_type_body->get_operation_mode() ==
		 PortTypeBody::PO_MESSAGE) {
	port_op.portref->error("Procedure-based operation `reply' is not "
	  "applicable to a message-based port of type `%s'",
	  port_type->get_typename().c_str());
      } else {
	port_op.portref->error("Port type `%s' does not have any incoming "
	  "signatures", port_type->get_typename().c_str());
      }
    }
    if (!signature_determined)
      signature = get_msg_sig_type(port_op.s.sendpar);
    if (!signature) signature = Type::get_pooltype(Type::T_ERROR);
    // checking the parameter (template instance)
    port_op.s.sendpar->chk(signature);
    signature = signature->get_type_refd_last();
    Type *return_type = 0;
    switch (signature->get_typetype()) {
    case Type::T_SIGNATURE:
      // the signature is known and correct
      if (signature->is_nonblocking_signature())
	error("Operation `reply' is not applicable to non-blocking signature "
	  "`%s'", signature->get_typename().c_str());
      else return_type = signature->get_signature_return_type();
      // checking the presence/absence of reply value
      if (port_op.s.replyval) {
	if (!return_type) {
	  port_op.s.replyval->error("Unexpected return value. Signature "
	    "`%s' does not have return type",
	    signature->get_typename().c_str());
	}
      } else if (return_type) {
	error("Missing return value. Signature `%s' returns type `%s'",
	  signature->get_typename().c_str(),
	  return_type->get_typename().c_str());
      }
    case Type::T_ERROR:
      break;
    default:
      port_op.s.sendpar->error("The type of parameter is `%s', which is not a "
	"signature", signature->get_typename().c_str());
    }
    // checking the reply value if present
    if (port_op.s.replyval) {
      Error_Context cntxt2(port_op.s.replyval, "In return value");
      if (!return_type) return_type = Type::get_pooltype(Type::T_ERROR);
      port_op.s.replyval->set_my_governor(return_type);
      return_type->chk_this_value_ref(port_op.s.replyval);
      return_type->chk_this_value(port_op.s.replyval, 0,
        Type::EXPECTED_DYNAMIC_VALUE, INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED,
        SUB_CHK);
    }
    // checking to clause
    chk_to_clause(port_type);
    // checking timestamp redirect
    chk_timestamp_redirect(port_type, true);
  }

  void Statement::chk_raise()
  {
    Error_Context cntxt(this, "In raise statement");
    // checking the port reference
    Type *port_type = chk_port_ref(port_op.portref);
    // determining the signature of the exception
    port_op.s.raise.signature =
      chk_signature_ref(port_op.s.raise.signature_ref);
    // checking whether the signature is present on the incoming list
    // of the respective port type
    if (port_type) {
      PortTypeBody *port_type_body = port_type->get_PortBody();
      TypeSet *in_sigs = port_type_body->get_in_sigs();
      if (port_type_body->get_operation_mode() ==
		 PortTypeBody::PO_MESSAGE) {
	port_op.portref->error("Procedure-based operation `raise' is not "
	  "applicable to a message-based port of type `%s'",
	  port_type->get_typename().c_str());
      } else if (in_sigs) {
	if (port_op.s.raise.signature) {
	  if (!in_sigs->has_type(port_op.s.raise.signature)) {
	    port_op.s.raise.signature_ref->error("Signature `%s' is not "
	      "present on the incoming list of port type `%s'",
	      port_op.s.raise.signature->get_typename().c_str(),
	      port_type->get_typename().c_str());
	  }
	} else if (in_sigs->get_nof_types() == 1) {
	  // if the signature is unknown and the port type has exactly one
	  // incoming signature then use that for further checking
	  port_op.s.raise.signature =
	    in_sigs->get_type_byIndex(0)->get_type_refd_last();
	}
      } else {
	port_op.portref->error("Port type `%s' does not have any incoming "
	  "signatures", port_type->get_typename().c_str());
      }
    }
    // determining the type of exception
    Type *exc_type = 0;
    bool exc_type_determined = false;
    if (port_op.s.raise.signature) {
      // the signature is known
      SignatureExceptions *exceptions =
	port_op.s.raise.signature->get_signature_exceptions();
      if (exceptions) {
	if (exceptions->get_nof_types() == 1) {
	  // the signature has exactly one exception type
	  // use that for checking
	  exc_type = exceptions->get_type_byIndex(0);
	} else {
	  // the signature has more than one exception types
	  exc_type = get_msg_sig_type(port_op.s.sendpar);
	  if (exc_type) {
	    size_t nof_comp_types =
	      exceptions->get_nof_compatible_types(exc_type);
	    if (nof_comp_types == 0) {
	      port_op.s.sendpar->error("Type `%s' is not present on the "
		"exception list of signature `%s'",
		exc_type->get_typename().c_str(),
		port_op.s.raise.signature->get_typename().c_str());
	    } else if (nof_comp_types > 1) {
	      port_op.s.sendpar->error("Type of the exception is ambiguous: "
		"`%s' is compatible with more than one exception types of "
		"signature `%s'", exc_type->get_typename().c_str(),
		port_op.s.raise.signature->get_typename().c_str());
	    }
	  } else {
	    port_op.s.sendpar->error("Cannot determine the type of the "
	      "exception");
	  }
	}
	exc_type_determined = true;
      } else {
	port_op.s.raise.signature_ref->error("Signature `%s' does not have "
	  "exceptions", port_op.s.raise.signature->get_typename().c_str());
      }
    }
    // determining the type of exception if it is not done so far
    if (!exc_type_determined) {
      exc_type = get_msg_sig_type(port_op.s.sendpar);
    }
    if (!exc_type) exc_type = Type::get_pooltype(Type::T_ERROR);
    // checking the exception template
    port_op.s.sendpar->chk(exc_type);
    // checking for invalid exception types
    exc_type = exc_type->get_type_refd_last();
    switch (exc_type->get_typetype()) {
    case Type::T_SIGNATURE:
      port_op.s.sendpar->error("The type of raise parameter is signature `%s', "
	"which cannot be an exception type",
	exc_type->get_typename().c_str());
      break;
    case Type::T_PORT:
      port_op.s.sendpar->error("The type of raise parameter is port type `%s', "
	"which cannot be an exception type",
	exc_type->get_typename().c_str());
      break;
    case Type::T_DEFAULT:
      port_op.s.sendpar->error("The type of raise parameter is the `default' "
	"type, which cannot be an exception type");
    default:
      break;
    }
    // checking for presence of wildcards in the template body
    port_op.s.sendpar->get_Template()->chk_specific_value(false);
    // checking to clause
    chk_to_clause(port_type);
    // checking timestamp redirect
    chk_timestamp_redirect(port_type, true);
  }

  void Statement::chk_receive()
  {
    // determining statement type
    const char *stmt_name = get_stmt_name();
    Error_Context cntxt(this, "In %s statement", stmt_name);
    // checking the port reference
    Type *port_type = NULL;
    if (port_op.translate) {
      PortScope* ps = my_sb->get_scope_port();
      if (ps) {
        port_type = ps->get_port_type();
      } else {
        error("Cannot determine the type of the port: Missing port clause on the function.");
      }
    } else {
      port_type = chk_port_ref(port_op.portref, port_op.anyfrom);
    }
    // checking the parameter and/or value redirect
    if (port_op.r.rcvpar) {
      // the receive parameter (template instance) is present
      // trying to determine type of the incoming message
      Type *msg_type = 0;
      bool msg_type_determined = false;
      if (port_type) {
	// the port reference is correct and the port type is known
	PortTypeBody *port_type_body = port_type->get_PortBody();
	TypeSet *in_msgs = port_type_body->get_in_msgs();
	if (in_msgs) {
          if (in_msgs->get_nof_types() == 1) {
	    // there is only one incoming message type
	    // use that for checking
	    msg_type = in_msgs->get_type_byIndex(0);
	  } else {
	    // there are more than one incoming message types
	    msg_type = get_msg_sig_type(port_op.r.rcvpar);
	    if (msg_type) {
	      size_t nof_comp_types =
		in_msgs->get_nof_compatible_types(msg_type);
	      if (nof_comp_types == 0) {
		port_op.r.rcvpar->error("Message type `%s' is not present on "
		  "the incoming list of port of type `%s'",
		  msg_type->get_typename().c_str(),
		  port_type->get_typename().c_str());
	      } else if (nof_comp_types > 1) {
		port_op.r.rcvpar->error("Type of the message is ambiguous: "
		  "`%s' is compatible with more than one incoming message "
		  "types of port type `%s'", msg_type->get_typename().c_str(),
		  port_type->get_typename().c_str());
	      }
	    } else {
	      port_op.r.rcvpar->error("Cannot determine the type of the "
	        "incoming message");
	    }
	  }
	  msg_type_determined = true;
	} else if (port_type_body->get_operation_mode() ==
	           PortTypeBody::PO_PROCEDURE) {
	  port_op.portref->error("Message-based operation `%s' is not "
	    "applicable to a procedure-based port of type `%s'", stmt_name,
	    port_type->get_typename().c_str());
	} else {
	  port_op.portref->error("Port type `%s' does not have any incoming "
	    "message types", port_type->get_typename().c_str());
	}
      } else if (!port_op.portref && port_op.translate == false) {
	// the statement refers to 'any port'
	port_op.r.rcvpar->error("Operation `any port.%s' cannot have parameter",
	  stmt_name);
	if (port_op.r.redirect.value) {
	  port_op.r.redirect.value->error("Operation `any port.%s' cannot have "
	    "value redirect", stmt_name);
	}
        if (port_op.r.redirect.index) {
          port_op.r.redirect.index->error("Operation `any port.%s' cannot have "
            "index redirect", stmt_name);
        }
      }
      if (!msg_type_determined) {
	msg_type = get_msg_sig_type(port_op.r.rcvpar);
      }
      if (!msg_type) msg_type = Type::get_pooltype(Type::T_ERROR);
      // check the template instance using the message type
      port_op.r.rcvpar->chk(msg_type);
      if (port_op.r.rcvpar->get_Template()->get_template_refd_last()->
          get_templatetype() == Template::ANY_OR_OMIT) {
        port_op.r.rcvpar->error("'*' cannot be used as a matching template "
          "for a '%s' operation", stmt_name);
      }
      // check the value redirect if it exists
      if (port_op.r.redirect.value != NULL) {
        port_op.r.redirect.value->chk(msg_type);
      }
    } else {
      // the statement does not have parameter
      if (port_type) {
        PortTypeBody *port_type_body = port_type->get_PortBody();
        if (!port_type_body->get_in_msgs()) {
          // the port type is known and it does not have incoming messages
	  if (port_type_body->get_operation_mode() ==
	      PortTypeBody::PO_PROCEDURE) {
	    port_op.portref->error("Message-based operation `%s' is not "
	      "applicable to a procedure-based port of type `%s'", stmt_name,
	      port_type->get_typename().c_str());
	  } else {
	    port_op.portref->error("Port type `%s' does not have any incoming "
	      "message types", port_type->get_typename().c_str());
	  }
	}
      }
      if (port_op.r.redirect.value) {
	port_op.r.redirect.value->error("Value redirect cannot be used without "
	  "receive parameter");
	port_op.r.redirect.value->chk_erroneous();
      }
    }
    // checking from clause and sender redirect
    chk_from_clause(port_type);
    if (port_op.r.redirect.index != NULL && port_op.portref != NULL) {
      Common::Assignment *t_ass = port_op.portref->get_refd_assignment();
      chk_index_redirect(port_op.r.redirect.index,
        t_ass != NULL ? t_ass->get_Dimensions() : NULL, port_op.anyfrom, "port");
    }
    // checking timestamp redirect
    chk_timestamp_redirect(port_type, false);

    // checking deterministic
    if (port_op.r.rcvpar) {
      port_op.r.rcvpar->chk_immutability();
    }
  }

  void Statement::chk_getcall()
  {
    // determining statement type
    const char *stmt_name = get_stmt_name();
    Error_Context cntxt(this, "In %s statement", stmt_name);
    // checking the port reference
    Type *port_type = chk_port_ref(port_op.portref, port_op.anyfrom);
    if (port_op.r.rcvpar) {
      // the parameter (signature template) is present
      // determining the signature of the argument
      Type *signature = 0;
      bool signature_determined = false;
      if (port_type) {
	// the port reference is correct and the port type is known
	PortTypeBody *port_type_body = port_type->get_PortBody();
	TypeSet *in_sigs = port_type_body->get_in_sigs();
	if (in_sigs) {
	  if (in_sigs->get_nof_types() == 1) {
	    // there is only one incoming signature
	    signature = in_sigs->get_type_byIndex(0);
	  } else {
	    // there are more than one incoming signatures
	    signature = get_msg_sig_type(port_op.r.rcvpar);
	    if (signature) {
	      if (!in_sigs->has_type(signature)) {
		port_op.r.rcvpar->error("Signature `%s' is not present on the "
		  "incoming list of port type `%s'",
		  signature->get_typename().c_str(),
		  port_type->get_typename().c_str());
	      }
	    } else {
	      port_op.r.rcvpar->error("Cannot determine the type of the "
		"signature");
	    }
	  }
	  signature_determined = true;
	} else if (port_type_body->get_operation_mode() ==
		   PortTypeBody::PO_MESSAGE) {
	  port_op.portref->error("Procedure-based operation `%s' is not "
	    "applicable to a message-based port of type `%s'", stmt_name,
	    port_type->get_typename().c_str());
	} else {
	  port_op.portref->error("Port type `%s' does not have any incoming "
	    "signatures", port_type->get_typename().c_str());
	}
      } else if (!port_op.portref) {
	// the statement refers to 'any port'
	port_op.r.rcvpar->error("Operation `any port.%s' cannot have parameter",
	  stmt_name);
	if (port_op.r.redirect.param) {
	  port_op.r.redirect.param->error("Operation `any port.%s' cannot "
	    "have parameter redirect", stmt_name);
	}
      }
      if (!signature_determined)
	signature = get_msg_sig_type(port_op.r.rcvpar);
      if (!signature) signature = Type::get_pooltype(Type::T_ERROR);
      // checking the parameter (template instance)
      port_op.r.rcvpar->chk(signature);
      // checking whether the argument is a signature template
      // and checking the parameter redirect if present
      signature = signature->get_type_refd_last();
      switch (signature->get_typetype()) {
      case Type::T_SIGNATURE:
	if (port_op.r.redirect.param)
	  port_op.r.redirect.param->chk(signature, false);
	break;
      case Type::T_ERROR:
	if (port_op.r.redirect.param)
	  port_op.r.redirect.param->chk_erroneous();
	break;
      default:
	port_op.r.rcvpar->error("The type of parameter is `%s', which is not "
	  "a signature", signature->get_typename().c_str());
	if (port_op.r.redirect.param)
	  port_op.r.redirect.param->chk_erroneous();
      }
    } else {
      // the statement does not have parameter
      if (port_type) {
        PortTypeBody *port_type_body = port_type->get_PortBody();
	if (!port_type_body->get_in_sigs()) {
          // the port type is known and it does not have incoming signatures
	  if (port_type_body->get_operation_mode() ==
	      PortTypeBody::PO_MESSAGE) {
	    port_op.portref->error("Procedure-based operation `%s' is not "
	      "applicable to a message-based port of type `%s'", stmt_name,
	      port_type->get_typename().c_str());
	  } else {
	    port_op.portref->error("Port type `%s' does not have any incoming "
	      "signatures", port_type->get_typename().c_str());
	  }
	}
      }
      if (port_op.r.redirect.param) {
	port_op.r.redirect.param->error("Parameter redirect cannot be used "
	  "without signature template");
	port_op.r.redirect.param->chk_erroneous();
      }
    }
    // checking from clause and sender redirect
    chk_from_clause(port_type);
    if (port_op.r.redirect.index != NULL && port_op.portref != NULL) {
      Common::Assignment *t_ass = port_op.portref->get_refd_assignment();
      chk_index_redirect(port_op.r.redirect.index,
        t_ass != NULL ? t_ass->get_Dimensions() : NULL, port_op.anyfrom, "port");
    }
    // checking timestamp redirect
    chk_timestamp_redirect(port_type, false);
  }

  void Statement::chk_getreply()
  {
    // determining statement type
    const char *stmt_name = get_stmt_name();
    Error_Context cntxt(this, "In %s statement", stmt_name);
    // checking the port reference
    Type *port_type = chk_port_ref(port_op.portref, port_op.anyfrom);
    if (port_op.r.rcvpar) {
      // the parameter (signature template) is present
      // determining the signature of the argument
      Type *signature = 0;
      bool signature_determined = false;
      if (port_type) {
	// the port reference is correct and the port type is known
	PortTypeBody *port_type_body = port_type->get_PortBody();
	if (port_type_body->getreply_allowed()) {
	  TypeSet *out_sigs = port_type_body->get_out_sigs();
	  if (out_sigs->get_nof_types() == 1) {
	    // there is only one outgoing signature
	    signature = out_sigs->get_type_byIndex(0);
	  } else {
	    // there are more than one outgoing signatures
	    signature = get_msg_sig_type(port_op.r.rcvpar);
	    if (signature) {
	      if (!out_sigs->has_type(signature)) {
		port_op.r.rcvpar->error("Signature `%s' is not present on the "
		  "outgoing list of port type `%s'",
		  signature->get_typename().c_str(),
		  port_type->get_typename().c_str());
	      }
	    } else {
	      port_op.r.rcvpar->error("Cannot determine the type of the "
		"signature");
	    }
	  }
	  signature_determined = true;
	} else if (port_type_body->get_operation_mode() ==
		   PortTypeBody::PO_MESSAGE) {
	  port_op.portref->error("Procedure-based operation `%s' is not "
	    "applicable to a message-based port of type `%s'", stmt_name,
	    port_type->get_typename().c_str());
	} else {
	  port_op.portref->error("Port type `%s' does not have any outgoing "
	    "signatures that support reply", port_type->get_typename().c_str());
	}
      } else if (!port_op.portref) {
	// the statement refers to 'any port'
	port_op.r.rcvpar->error("Operation `any port.%s' cannot have parameter",
	  stmt_name);
	if (port_op.r.getreply_valuematch) {
	  port_op.r.getreply_valuematch->error("Operation `any port.%s' cannot "
	    "have value match", stmt_name);
	}
	if (port_op.r.redirect.value) {
	  port_op.r.redirect.value->error("Operation `any port.%s' cannot "
	    "have value redirect", stmt_name);
	}
	if (port_op.r.redirect.param) {
	  port_op.r.redirect.param->error("Operation `any port.%s' cannot "
	    "have parameter redirect", stmt_name);
	}
      }
      if (!signature_determined)
	signature = get_msg_sig_type(port_op.r.rcvpar);
      if (!signature) signature = Type::get_pooltype(Type::T_ERROR);
      // checking the parameter (template instance)
      port_op.r.rcvpar->chk(signature);
      // checking whether the argument is a signature template
      // checking the parameter redirect if present
      // and determining the return type of the signature
      signature = signature->get_type_refd_last();
      Type *return_type = 0;
      switch (signature->get_typetype()) {
      case Type::T_SIGNATURE:
	if (signature->is_nonblocking_signature())
	  error("Operation `%s' is not applicable to non-blocking signature "
	    "`%s'", stmt_name, signature->get_typename().c_str());
	else return_type = signature->get_signature_return_type();
	if (port_op.r.redirect.param)
	  port_op.r.redirect.param->chk(signature, true);
	if (!return_type) {
	  if (port_op.r.getreply_valuematch) {
	    port_op.r.getreply_valuematch->error("Value match cannot be used "
	      "because signature `%s' does not have return type",
	      signature->get_typename().c_str());
	  }
	  if (port_op.r.redirect.value) {
	    port_op.r.redirect.value->error("Value redirect cannot be used "
	      "because signature `%s' does not have return type",
	      signature->get_typename().c_str());
	  }
	}
	break;
      case Type::T_ERROR:
	if (port_op.r.redirect.param)
	  port_op.r.redirect.param->chk_erroneous();
	break;
      default:
	port_op.r.rcvpar->error("The type of parameter is `%s', which is not "
	  "a signature", signature->get_typename().c_str());
	if (port_op.r.redirect.param)
	  port_op.r.redirect.param->chk_erroneous();
      }
      // checking the value match if present
      if (port_op.r.getreply_valuematch) {
        Error_Context cntxt2(port_op.s.replyval, "In value match");
        if (!return_type) return_type = Type::get_pooltype(Type::T_ERROR);
        port_op.r.getreply_valuematch->chk(return_type);
        if (port_op.r.getreply_valuematch->get_Template()->get_template_refd_last()->
            get_templatetype() == Template::ANY_OR_OMIT) {
          port_op.r.getreply_valuematch->error("'*' cannot be used as a return "
            "value matching template for a '%s' operation", stmt_name);
        }
      }
      // checking the value redirect if present
      if (port_op.r.redirect.value != NULL) {
        port_op.r.redirect.value->chk(return_type);
      }
    } else {
      // the statement does not have parameter (value match is also omitted)
      if (port_type) {
        PortTypeBody *port_type_body = port_type->get_PortBody();
	if (!port_type_body->getreply_allowed()) {
          // the port type is known and it does not have outgoing signatures
	  if (port_type_body->get_operation_mode() ==
	      PortTypeBody::PO_MESSAGE) {
	    port_op.portref->error("Procedure-based operation `%s' is not "
	      "applicable to a message-based port of type `%s'", stmt_name,
	      port_type->get_typename().c_str());
	  } else {
	    port_op.portref->error("Port type `%s' does not have any outgoing "
	      "signatures that support reply",
	      port_type->get_typename().c_str());
	  }
	}
      }
      if (port_op.r.redirect.value) {
	port_op.r.redirect.value->error("Value redirect cannot be used "
	  "without signature template");
  port_op.r.redirect.value->chk_erroneous();
      }
      if (port_op.r.redirect.param) {
	port_op.r.redirect.param->error("Parameter redirect cannot be used "
	  "without signature template");
	port_op.r.redirect.param->chk_erroneous();
      }
    }
    // checking from clause and sender redirect
    chk_from_clause(port_type);
    if (port_op.r.redirect.index != NULL && port_op.portref != NULL) {
      Common::Assignment *t_ass = port_op.portref->get_refd_assignment();
      chk_index_redirect(port_op.r.redirect.index,
        t_ass != NULL ? t_ass->get_Dimensions() : NULL, port_op.anyfrom, "port");
    }
    // checking timestamp redirect
    chk_timestamp_redirect(port_type, false);
  }

  void Statement::chk_catch()
  {
    // determining statement type
    const char *stmt_name = get_stmt_name();
    Error_Context cntxt(this, "In %s statement", stmt_name);
    // checking the port reference
    Type *port_type = chk_port_ref(port_op.portref, port_op.anyfrom);
    // checking the signature reference, parameter and/or value redirect
    if (port_op.r.ctch.signature_ref) {
      // the signature reference is present
      port_op.r.ctch.signature =
	chk_signature_ref(port_op.r.ctch.signature_ref);
      // checking whether the signature is present on the incoming list
      // of the respective port type
      if (port_type) {
        PortTypeBody *port_type_body = port_type->get_PortBody();
	if (port_type_body->catch_allowed()) {
	  TypeSet *out_sigs = port_type_body->get_out_sigs();
	  if (port_op.r.ctch.signature) {
	    if (!out_sigs->has_type(port_op.r.ctch.signature)) {
	      port_op.r.ctch.signature_ref->error("Signature `%s' is not "
		"present on the outgoing list of port type `%s'",
		port_op.r.ctch.signature->get_typename().c_str(),
		port_type->get_typename().c_str());
	    }
	  } else if (out_sigs->get_nof_types() == 1) {
	    // if the signature is unknown and the port type has exactly one
	    // outgoing signature then use that for further checking
	    port_op.r.ctch.signature =
	      out_sigs->get_type_byIndex(0)->get_type_refd_last();
	  }
	} else if (port_type_body->get_operation_mode() ==
		   PortTypeBody::PO_MESSAGE) {
	  port_op.portref->error("Procedure-based operation `%s' is not "
	    "applicable to a message-based port of type `%s'", stmt_name,
	    port_type->get_typename().c_str());
	} else {
	  port_op.portref->error("Port type `%s' does not have any outgoing "
	    "signatures that support exceptions",
	    port_type->get_typename().c_str());
	}
      } else if (!port_op.portref) {
	// the statement refers to 'any port'
	port_op.r.rcvpar->error("Operation `any port.%s' cannot have parameter",
	  stmt_name);
	if (port_op.r.redirect.value) {
	  port_op.r.redirect.value->error("Operation `any port.%s' cannot have "
	    "value redirect", stmt_name);
	}
      }
      // the receive parameter (template instance) must be also present
      // trying to determine type of the exception
      Type *exc_type = 0;
      bool exc_type_determined = false;
      if (port_op.r.ctch.signature) {
	// the signature is known
	SignatureExceptions *exceptions =
	  port_op.r.ctch.signature->get_signature_exceptions();
	if (exceptions) {
	  if (exceptions->get_nof_types() == 1) {
	    // the signature has exactly one exception type
	    // use that for checking
	    exc_type = exceptions->get_type_byIndex(0);
	  } else {
	    // the signature has more than one exception types
	    exc_type = get_msg_sig_type(port_op.r.rcvpar);
	    if (exc_type) {
	      size_t nof_comp_types =
		exceptions->get_nof_compatible_types(exc_type);
	      if (nof_comp_types == 0) {
		port_op.r.rcvpar->error("Type `%s' is not present on the "
		  "exception list of signature `%s'",
		  exc_type->get_typename().c_str(),
		  port_op.r.ctch.signature->get_typename().c_str());
	      } else if (nof_comp_types > 1) {
		port_op.r.rcvpar->error("Type of the exception is ambiguous: "
		  "`%s' is compatible with more than one exception types of "
		  "signature `%s'", exc_type->get_typename().c_str(),
		  port_op.r.ctch.signature->get_typename().c_str());
	      }
	    } else {
	      port_op.r.rcvpar->error("Cannot determine the type of the "
		"exception");
	    }
	  }
	  exc_type_determined = true;
	} else {
	  port_op.r.ctch.signature_ref->error("Signature `%s' does not have "
	    "exceptions", port_op.r.ctch.signature->get_typename().c_str());
	}
      }
      if (!exc_type_determined) {
	exc_type = get_msg_sig_type(port_op.r.rcvpar);
      }
      if (!exc_type) exc_type = Type::get_pooltype(Type::T_ERROR);
      // check the template instance using the exception type
      port_op.r.rcvpar->chk(exc_type);
      if (port_op.r.rcvpar->get_Template()->get_template_refd_last()->
          get_templatetype() == Template::ANY_OR_OMIT) {
        port_op.r.rcvpar->error("'*' cannot be used as a matching template for "
          "a '%s' operation", stmt_name);
      }
      // check the value redirect if it exists
      if (port_op.r.redirect.value != NULL) {
        port_op.r.redirect.value->chk(exc_type);
      }
      // checking for invalid exception types
      exc_type = exc_type->get_type_refd_last();
      switch (exc_type->get_typetype()) {
      case Type::T_SIGNATURE:
	port_op.r.rcvpar->error("The type of catch parameter is signature "
	  "`%s', which cannot be an exception type",
	  exc_type->get_typename().c_str());
	break;
      case Type::T_PORT:
	port_op.r.rcvpar->error("The type of catch parameter is port type "
	  "`%s', which cannot be an exception type",
	  exc_type->get_typename().c_str());
	break;
      case Type::T_DEFAULT:
	port_op.r.rcvpar->error("The type of catch parameter is the `default' "
	  "type, which cannot be an exception type");
      default:
	break;
      }
    } else {
      // the statement does not have signature reference
      if (port_op.r.ctch.timeout) {
	// the parameter is timeout
	if (port_op.portref) {
	  // the port reference is present
	  if (port_type) {
            PortTypeBody *port_type_body = port_type->get_PortBody();
	    if (!port_type_body->getreply_allowed()) {
              // the port type is known and it does not allow blocking calls
	      if (port_type_body->get_operation_mode() ==
		  PortTypeBody::PO_MESSAGE) {
		port_op.portref->error("Timeout exception cannot be caught on "
		  "a message-based port of type `%s'",
		  port_type->get_typename().c_str());
	      } else {
		port_op.portref->error("Timeout exception cannot be caught on "
		  "a port of type `%s', which does not have any outgoing "
		  "signatures that allow blocking calls",
		  port_type->get_typename().c_str());
	      }
	    }
	  }
	} else error("Timeout exception cannot be caught on `any port'");
	if (!port_op.r.ctch.in_call)
	  error("Catching of `timeout' exception is not allowed in this "
	    "context. It is permitted only in the response and exception "
	    "handling part of `call' operations");
	else if (!port_op.r.ctch.call_has_timer)
	  error("Catching of `timeout' exception is not allowed because the "
	    "previous `call' operation does not have timer");
	if (port_op.r.fromclause) port_op.r.fromclause->error(
	  "Operation `catch(timeout)' cannot have from clause");
	if (port_op.r.redirect.sender) port_op.r.redirect.sender->error(
	  "Operation `catch(timeout)' cannot have sender redirect");
      } else {
	// the operation does not have any parameter
	if (port_type) {
          PortTypeBody *port_type_body = port_type->get_PortBody();
	  if (!port_type_body->catch_allowed()) {
            // the port type is known and it does not have outgoing signatures
	    if (port_type_body->get_operation_mode() ==
		PortTypeBody::PO_MESSAGE) {
	      port_op.portref->error("Procedure-based operation `%s' is not "
		"applicable to a message-based port of type `%s'", stmt_name,
		port_type->get_typename().c_str());
	    } else {
	      port_op.portref->error("Port type `%s' does not have any "
		"outgoing signatures that support exceptions",
		port_type->get_typename().c_str());
	    }
	  }
	}
      }
      if (port_op.r.redirect.value) {
	// the statement does not have any parameter,
	// but the value redirect is present
	port_op.r.redirect.value->error("Value redirect cannot be used without "
	  "signature and parameter");
	port_op.r.redirect.value->chk_erroneous();
      }
    }
    // checking from clause and sender redirect
    chk_from_clause(port_type);
    if (port_op.r.redirect.index != NULL && port_op.portref != NULL) {
      Common::Assignment *t_ass = port_op.portref->get_refd_assignment();
      chk_index_redirect(port_op.r.redirect.index,
        t_ass != NULL ? t_ass->get_Dimensions() : NULL, port_op.anyfrom, "port");
    }
    // checking timestamp redirect
    chk_timestamp_redirect(port_type, false);
  }

  void Statement::chk_check()
  {
    Error_Context cntxt(this, "In check statement");
    Type *port_type = chk_port_ref(port_op.portref, port_op.anyfrom);
    if (port_type && !port_type->get_PortBody()->has_queue()) {
      // the port type is known and it does not have incoming queue
      port_op.portref->error("Port type `%s' does not have incoming queue "
	"because it has neither incoming messages nor incoming or outgoing "
	"signatures", port_type->get_typename().c_str());
    }
    // checking from clause and sender redirect
    chk_from_clause(port_type);
    if (port_op.r.redirect.index != NULL && port_op.portref != NULL) {
      Common::Assignment *t_ass = port_op.portref->get_refd_assignment();
      chk_index_redirect(port_op.r.redirect.index,
        t_ass != NULL ? t_ass->get_Dimensions() : NULL, port_op.anyfrom, "port");
    }
    // checking timestamp redirect
    chk_timestamp_redirect(port_type, false);
  }

  void Statement::chk_clear()
  {
    Error_Context cntxt(this, "In clear port statement");
    Type *port_type = chk_port_ref(port_op.portref);
    if (port_type && !port_type->get_PortBody()->has_queue()) {
      // the port type is known and it does not have incoming queue
      port_op.portref->warning("Port type `%s' does not have incoming queue "
	"because it has neither incoming messages nor incoming or outgoing "
	"signatures", port_type->get_typename().c_str());
    }
  }

  void Statement::chk_start_stop_port()
  {
    Error_Context cntxt(this, "In %s statement", get_stmt_name());
    chk_port_ref(port_op.portref);
  }

  void Statement::chk_start_comp()
  {
    Error_Context cntxt(this, "In start test component statement");
    Type *comp_type = chk_comp_ref(comp_op.compref, false, false);
    Common::Assignment *t_ass = comp_op.funcinstref->get_refd_assignment_last();
    if (!t_ass) return;
    // checking whether the referred definition is a function
    Common::Assignment::asstype_t asstype = t_ass->get_asstype();
    switch (asstype) {
    case Common::Assignment::A_FUNCTION:
    case Common::Assignment::A_FUNCTION_RVAL:
    case Common::Assignment::A_FUNCTION_RTEMP:
      break;
    default:
      comp_op.funcinstref->error("Reference to a function was expected in the "
	"argument instead of %s", t_ass->get_description().c_str());
      return;
    }
    Def_Function *t_func = dynamic_cast<Def_Function*>(t_ass);
    if (!t_func) FATAL_ERROR("Statement::chk_start_comp()");
    // checking startability
    if (!t_func->chk_startable(this)) return;
    // checking the 'runs on' clause against the type of component reference
    Type *runs_on_type = t_func->get_RunsOnType();
    if (!comp_type || !runs_on_type) return;
    if (!runs_on_type->is_compatible(comp_type, NULL, NULL))
      comp_op.compref->error("Component type mismatch: The component reference "
        "is of type `%s', but %s runs on `%s'",
	comp_type->get_typename().c_str(), t_func->get_description().c_str(),
	runs_on_type->get_typename().c_str());
    // checking the return type
    switch (asstype) {
    case Common::Assignment::A_FUNCTION_RTEMP:
      comp_op.funcinstref->warning("Function `%s' returns a template of type "
	"`%s', which cannot be retrieved when the test component terminates",
	t_func->get_fullname().c_str(),
	t_func->get_Type()->get_typename().c_str());
      break;
    case Common::Assignment::A_FUNCTION_RVAL: {
      bool return_type_correct = false;
      Type *return_type = t_func->get_Type();
      for (Type *t = return_type; ; t = t->get_type_refd()) {
	if (t->has_done_attribute()) {
	  return_type_correct = true;
	  break;
	} else if (!t->is_ref()) break;
      }
      if (!return_type_correct)
        comp_op.funcinstref->warning("The return type of %s is `%s', which "
	  "does not have the `done' extension attribute. When the test "
	  "component terminates the returned value cannot be retrieved with "
	  "a `done' operation", t_func->get_description().c_str(),
	  return_type->get_typename().c_str());
      }
    default:
      break;
    }
  }

  void Statement::chk_start_comp_refd()
  {
    Error_Context cntxt(this, "In start test component statement");
    Type *comp_type = chk_comp_ref(comp_op.compref, false, false);
    switch(comp_op.derefered.value->get_valuetype()){
    case Value::V_REFER:
      comp_op.derefered.value->error("A value of a function type was expected "
        "in the argument instead of a `refers' statement,"
        " which does not specify any function type");
      return;
    case Value::V_TTCN3_NULL:
      comp_op.derefered.value->error("A value of a function type was expected "
        "in the argument instead of a `null' value,"
        " which does not specify any function type");
      return;
    default:
      break;
    }
    Type *f_type = comp_op.derefered.value->get_expr_governor_last();
    if (!f_type) return;
    switch (f_type->get_typetype()) {
    case Type::T_ERROR:
      return;
    case Type::T_FUNCTION:
      break;
    default:
      comp_op.derefered.value->error("A value of type function was expected "
	"in the argument instead of `%s'", f_type->get_typename().c_str());
      return;
    }
    if (f_type->get_fat_runs_on_self()) {
      fau_refd.value->error("The argument cannot be a function reference with "
        "'runs on self' clause");
      return;
    }
    if(!f_type->chk_startability(this)) return;
    Type *runs_on_type = f_type->get_fat_runs_on_type();
    if (!comp_type || !runs_on_type) return;
    if (!runs_on_type->is_compatible(comp_type, NULL, NULL))
      comp_op.compref->error("Component type mismatch: The component reference "
        "is of type `%s', but functions of type `%s' run on `%s'",
        comp_type->get_typename().c_str(), f_type->get_typename().c_str(),
        runs_on_type->get_typename().c_str());
    Type *return_type = f_type->get_function_return_type();
    if (return_type) {
      if (f_type->get_returns_template()) {
	comp_op.derefered.value->warning("Functions of type `%s' return a "
	  "template of type `%s', which cannot be retrieved when the test "
	  "component terminates", f_type->get_typename().c_str(),
	  return_type->get_typename().c_str());
      } else {
	bool return_type_correct = false;
	for (Type *t = return_type; ; t = t->get_type_refd()) {
          if (t->has_done_attribute()) {
            return_type_correct = true;
            break;
          } else if (!t->is_ref()) break;
	}
	if (!return_type_correct)
	  comp_op.derefered.value->warning("The return type of function type "
	    "`%s' is `%s', which does not have the `done' extension attribute. "
	    "When the test component terminates the returned value cannot be "
	    "retrieved with a `done' operation", f_type->get_typename().c_str(),
	    return_type->get_typename().c_str());
      }
    }
    ActualParList *parlist = new ActualParList;
    Ttcn::FormalParList *fp_list = f_type->get_fat_parameters();
    if(fp_list->fold_named_and_chk(comp_op.derefered.t_list1, parlist)) {
      delete parlist;
      delete comp_op.derefered.t_list1;
      comp_op.derefered.ap_list2 = 0;
    } else {
      delete comp_op.derefered.t_list1;
      parlist->set_fullname(get_fullname());
      parlist->set_my_scope(my_sb);
      comp_op.derefered.ap_list2 = parlist;
    }
  }

  void Statement::chk_stop_kill_comp()
  {
    Error_Context cntxt(this, "In %s statement", get_stmt_name());
    chk_comp_ref(comp_op.compref, true, false);
  }

  void Statement::chk_done()
  {
    Error_Context cntxt(this, "In done statement");
    Type* ref_type = chk_comp_ref(comp_op.compref, false, false, comp_op.any_from);
    if (!comp_op.compref) return;
    // value returning done can be used only when the statement contains a
    // specific component reference
    if (comp_op.donereturn.donematch) {
      // try to determine the type of the return value
      Type *return_type = get_msg_sig_type(comp_op.donereturn.donematch);
      if (return_type) {
	bool return_type_correct = false;
	for (Type *t = return_type; ; t = t->get_type_refd()) {
	  if (t->has_done_attribute()) {
	    return_type_correct = true;
	    break;
	  } else if (!t->is_ref()) break;
	}
	if (!return_type_correct) {
		error("Return type `%s' does not have `done' extension attribute",
		return_type->get_typename().c_str());
		return_type = Type::get_pooltype(Type::T_ERROR);
	}
        if (comp_op.any_from) {
          return_type->get_type_refd_last()->set_needs_any_from_done();
        }
      } else {
	comp_op.donereturn.donematch->error("Cannot determine the return type "
	  "for value returning done");
	return_type = Type::get_pooltype(Type::T_ERROR);
      }
      comp_op.donereturn.donematch->chk(return_type);
      if (comp_op.donereturn.donematch->get_Template()->get_template_refd_last()->
          get_templatetype() == Template::ANY_OR_OMIT) {
        comp_op.donereturn.donematch->error("'*' cannot be used as a matching "
          "template for a 'done' operation");
      }
      if (comp_op.donereturn.redirect != NULL) {
        comp_op.donereturn.redirect->chk(return_type);
      }
    } else if (comp_op.donereturn.redirect) {
      // if there is no matching template, then the value redirect stores the
      // PTC's verdict instead of the return value
      comp_op.donereturn.redirect->chk_verdict_only();
    }
    if (comp_op.index_redirect != NULL && ref_type != NULL) {
      ArrayDimensions dummy;
      ref_type = ref_type->get_type_refd_last();
      while (ref_type->get_typetype() == Type::T_ARRAY) {
        dummy.add(ref_type->get_dimension()->clone());
        ref_type = ref_type->get_ofType()->get_type_refd_last();
      }
      chk_index_redirect(comp_op.index_redirect, &dummy, comp_op.any_from, 
        "component");
    }
  }

  void Statement::chk_killed()
  {
    Error_Context cntxt(this, "In killed statement");
    Type* ref_type = chk_comp_ref(comp_op.compref, false, false, comp_op.any_from);
    if (comp_op.index_redirect != NULL && ref_type != NULL) {
      ArrayDimensions dummy;
      ref_type = ref_type->get_type_refd_last();
      while (ref_type->get_typetype() == Type::T_ARRAY) {
        dummy.add(ref_type->get_dimension()->clone());
        ref_type = ref_type->get_ofType()->get_type_refd_last();
      }
      chk_index_redirect(comp_op.index_redirect, &dummy, comp_op.any_from, 
        "component");
    }
  }

  void Statement::chk_connect()
  {
    Error_Context cntxt(this, "In %s statement", get_stmt_name());
    // checking endpoints
    Type *pt1, *pt2;
    PortTypeBody *ptb1, *ptb2;
    {
      Error_Context cntxt2(config_op.compref1, "In first endpoint");
      pt1 = chk_conn_endpoint(config_op.compref1, config_op.portref1, false);
      ptb1 = pt1 ? pt1->get_PortBody() : 0;
    }
    {
      Error_Context cntxt2(config_op.compref2, "In second endpoint");
      pt2 = chk_conn_endpoint(config_op.compref2, config_op.portref2, false);
      ptb2 = pt2 ? pt2->get_PortBody() : 0;
    }
    // checking consistency
    if (!ptb1 || !ptb2) return;
    if (!ptb1->connect_can_receive_or_send(ptb2)) {
      warning("Neither port type `%s' nor port type `%s' can send messages.",
        pt1->get_typename().c_str(), pt2->get_typename().c_str());
    }
    if (!ptb1->is_connectable(ptb2) ||
	(ptb1 != ptb2 && !ptb2->is_connectable(ptb1))) {
      error("The connection between port types `%s' and `%s' is not consistent",
        pt1->get_typename().c_str(), pt2->get_typename().c_str());
      ptb1->report_connection_errors(ptb2);
      if (ptb1 != ptb2) ptb2->report_connection_errors(ptb1);
    }
    
    {
      Error_Context cntxt2(config_op.compref1, "In first endpoint");
      if (ptb1->get_testport_type() == PortTypeBody::TP_ADDRESS) {
        error("An address supporting port cannot be used in a connect operation");
      }
    }
    
    {
      Error_Context cntxt2(config_op.compref1, "In second endpoint");
      if (ptb2->get_testport_type() == PortTypeBody::TP_ADDRESS) {
        error("An address supporting port cannot be used in a connect operation");
      }
    }
  }

  void Statement::chk_map()
  {
    Error_Context cntxt(this, "In %s statement", get_stmt_name());
    // checking endpoints
    Type *pt1, *pt2;
    PortTypeBody *ptb1, *ptb2;
    bool cref1_is_tc = false, cref1_is_system = false;
    bool cref2_is_tc = false, cref2_is_system = false;
    {
      Error_Context cntxt2(config_op.compref1, "In first endpoint");
      pt1 = chk_conn_endpoint(config_op.compref1, config_op.portref1, true);
      if (pt1) {
        ptb1 = pt1->get_PortBody();
      } else ptb1 = 0;
      Value *cref1 = config_op.compref1->get_value_refd_last();
      if (cref1->get_valuetype() == Value::V_EXPR) {
        switch (cref1->get_optype()) {
        case Value::OPTYPE_COMP_MTC:
        case Value::OPTYPE_COMP_SELF:
        case Value::OPTYPE_COMP_CREATE:
	        cref1_is_tc = true;
          break;
        case Value::OPTYPE_COMP_SYSTEM:
          cref1_is_system = true;
        default:
          break;
        }
      }
    }
    {
      Error_Context cntxt2(config_op.compref2, "In second endpoint");
      pt2 = chk_conn_endpoint(config_op.compref2, config_op.portref2, true);
      if (pt2) {
        ptb2 = pt2->get_PortBody();
      } else ptb2 = 0;
      Value *cref2 = config_op.compref2->get_value_refd_last();
      if (cref2->get_valuetype() == Value::V_EXPR) {
        switch (cref2->get_optype()) {
        case Value::OPTYPE_COMP_MTC:
        case Value::OPTYPE_COMP_SELF:
        case Value::OPTYPE_COMP_CREATE:
	        cref2_is_tc = true;
          break;
        case Value::OPTYPE_COMP_SYSTEM:
          cref2_is_system = true;
        default:
          break;
        }
      }
    }
    if (cref1_is_tc && cref2_is_tc) {
      error("Both endpoints of the mapping are test component ports");
      return;
    }
    if (cref1_is_system && cref2_is_system) {
      error("Both endpoints of the mapping are system ports");
      return;
    }
    // checking consistency
    if (!ptb1 || !ptb2) {
      if (ptb1 && ptb1->is_internal()) {
        Error_Context cntxt2(config_op.compref1, "In first endpoint");
        config_op.portref1->warning("Port type `%s' was marked as `internal'",
                pt1->get_typename().c_str());
      }
      if (ptb2 && ptb2->is_internal()) {
        Error_Context cntxt2(config_op.compref2, "In second endpoint");
        config_op.portref2->warning("Port type `%s' was marked as `internal'",
                pt2->get_typename().c_str());
      }
      if ((ptb1 != NULL && !ptb1->is_legacy() &&
           ptb1->get_type() == PortTypeBody::PT_USER) ||
          (ptb2 != NULL && !ptb2->is_legacy() &&
           ptb2->get_type() == PortTypeBody::PT_USER)) {
        note("This mapping is not done in translation mode, because the %s "
          "endpoint is unknown",
          ptb1 != NULL ? "second" : "first");
      }
      chk_map_params(cref1_is_system ? ptb1 : (cref2_is_system ? ptb2 : NULL));
      return;
    }
    if (cref1_is_tc || cref2_is_system) {
      // The check for safe mapping was already checked in
      // PortTypeBody::chk_map_translation()
      config_op.translate = !ptb1->is_legacy() && ptb1->is_translate(ptb2);
      if (!config_op.translate && !ptb1->is_mappable(ptb2)) {
        error("The mapping between test component port type `%s' and system "
          "port type `%s' is not consistent", pt1->get_typename().c_str(),
          pt2->get_typename().c_str());
        ptb1->report_mapping_errors(ptb2);
      }
      if (!config_op.translate && !ptb1->map_can_receive_or_send(ptb2)) {
        warning("Port type `%s' cannot send or receive from system port type `%s'.",
          pt1->get_typename().c_str(), pt2->get_typename().c_str());
      }
    } else if (cref2_is_tc || cref1_is_system) {
      config_op.translate = !ptb2->is_legacy() && ptb2->is_translate(ptb1);
      if (!config_op.translate && !ptb2->is_mappable(ptb1)) {
        error("The mapping between system port type `%s' and test component "
          "port type `%s' is not consistent", pt1->get_typename().c_str(),
          pt2->get_typename().c_str());
        ptb2->report_mapping_errors(ptb1);
      }
      if (!config_op.translate && !ptb2->map_can_receive_or_send(ptb1)) {
        warning("Port type `%s' cannot send or receive from system port type `%s'.",
          pt2->get_typename().c_str(), pt1->get_typename().c_str());
      }
    } else {
      // we have no idea which one is the system port
      bool first_is_mapped_to_second = !ptb1->is_legacy() && ptb1->is_translate(ptb2);
      bool second_is_mapped_to_first = !ptb2->is_legacy() && ptb2->is_translate(ptb1);
      config_op.translate = first_is_mapped_to_second || second_is_mapped_to_first;
      if (!config_op.translate && !ptb1->is_mappable(ptb2) && !ptb2->is_mappable(ptb1)) {
        error("The mapping between port types `%s' and `%s' is not consistent",
                pt1->get_typename().c_str(), pt2->get_typename().c_str());
      }
    }
    if (!config_op.translate) {
      if (ptb1->is_internal()) {
        Error_Context cntxt2(config_op.compref1, "In first endpoint");
        config_op.portref1->warning("Port type `%s' was marked as `internal'",
                pt1->get_typename().c_str());
      }
      if (ptb2->is_internal()) {
        Error_Context cntxt2(config_op.compref2, "In second endpoint");
        config_op.portref2->warning("Port type `%s' was marked as `internal'",
                pt2->get_typename().c_str());
      }
      if ((!ptb1->is_legacy() && ptb1->get_type() == PortTypeBody::PT_USER) ||
          (!ptb2->is_legacy() && ptb2->get_type() == PortTypeBody::PT_USER)) {
        note("This mapping is not done in translation mode");
      }
    } else {
      if (!config_op.compref1->get_expr_governor(Type::EXPECTED_DYNAMIC_VALUE)) {
        if (statementtype == S_MAP) {
          config_op.compref1->warning(
            "Cannot determine the type of the component in the first parameter."
            "The port translation will not work.");
        }
      }
      if (!config_op.compref2->get_expr_governor(Type::EXPECTED_DYNAMIC_VALUE)) {
        if (statementtype == S_MAP) {
          config_op.compref2->warning(
            "Cannot determine the type of the component in the second parameter."
            "The port translation will not work.");
        }
      }
    }

    chk_map_params(cref1_is_system ? ptb1 : (cref2_is_system ? ptb2 : NULL));
  }

  void Statement::chk_map_params(PortTypeBody* p_system_port)
  {
    if (config_op.parsed_params == NULL) {
      return;
    }
    if (p_system_port != NULL) {
      config_op.fp_list = p_system_port->get_map_parameters(statementtype == S_MAP);
    }
    else {
      error("Cannot determine system component in `%s' operation with "
        "`param' clause", get_stmt_name());
    }
    if (config_op.fp_list != NULL) {
      ActualParList* parlist = new ActualParList;
      if (config_op.fp_list->fold_named_and_chk(config_op.parsed_params, parlist)) {
        delete parlist;
        delete config_op.parsed_params;
        config_op.ap_list = NULL;
      } else {
        delete config_op.parsed_params;
        parlist->set_fullname(get_fullname());
        parlist->set_my_scope(my_sb);
        config_op.ap_list = parlist;
      }
    }
  }

  void Statement::chk_start_timer()
  {
    Error_Context cntxt(this, "In start timer statement");
    chk_timer_ref(timer_op.timerref);
    if (timer_op.value) {
      // check the actual duration
      timer_op.value->chk_expr_float(Type::EXPECTED_DYNAMIC_VALUE);
      Value *t_val = timer_op.value->get_value_refd_last();
      if (t_val->get_valuetype() == Value::V_REAL) {
        ttcn3float v_real = t_val->get_val_Real();
        if (v_real < 0.0) {
          timer_op.value->error("The timer duration is negative: `%s'",
                                Real2string(v_real).c_str());
        } else if (isSpecialFloatValue(v_real)) {
          timer_op.value->error("The timer duration cannot be %s",
                                Real2string(v_real).c_str());
        }
      }
    } else {
      // check whether the timer has default duration
      Common::Assignment *t_ass = timer_op.timerref->get_refd_assignment();
      if (t_ass && t_ass->get_asstype() == Common::Assignment::A_TIMER) {
        Def_Timer *t_def_timer = dynamic_cast<Def_Timer*>(t_ass);
	if (!t_def_timer) FATAL_ERROR("Statement::chk_start_timer()");
	if (!t_def_timer->has_default_duration(
	    timer_op.timerref->get_subrefs()))
	  error("Missing duration: %s does not have default duration",
	    t_ass->get_description().c_str());
      }
    }
  }
  
  void Statement::chk_stop_timer()
  {
    Error_Context cntxt(this, "In stop timer statement");
    chk_timer_ref(timer_op.timerref);
  }

  void Statement::chk_timer_timeout()
  {
    Error_Context cntxt(this, "In timeout statement");
    chk_timer_ref(timer_op.timerref, timer_op.any_from);
    if (timer_op.index_redirect != NULL && timer_op.timerref != NULL) {
      Common::Assignment* t_ass = timer_op.timerref->get_refd_assignment();
      chk_index_redirect(timer_op.index_redirect,
        t_ass != NULL ? t_ass->get_Dimensions() : NULL, timer_op.any_from, "timer");
    }
  }

  void Statement::chk_setverdict()
  {
    Error_Context cntxt(this, "In setverdict statement");
    if(!my_sb->get_my_def())
      error("Setverdict statement is not allowed in the control part");
    setverdict.verdictval->chk_expr_verdict(Type::EXPECTED_DYNAMIC_VALUE);
    Value *t_val = setverdict.verdictval->get_value_refd_last();
    if (t_val->get_valuetype() == Value::V_VERDICT &&
        t_val->get_val_verdict() == Value::Verdict_ERROR) {
      setverdict.verdictval->error("Error verdict cannot be set by the setverdict "
	"operation");
    }
  }

  void Statement::chk_execute()
  {
    Error_Context cntxt(this, "In execute statement");
    Reference *ref=testcase_inst.tcref;
    Common::Assignment *t_ass=ref->get_refd_assignment();
    if(!t_ass) goto error;
    if(t_ass->get_asstype()!=Common::Assignment::A_TESTCASE) {
      ref->error("Reference to a testcase was expected in the argument"
                 " instead of %s", t_ass->get_description().c_str());
      goto error;
    }
    if(my_sb->get_scope_runs_on()) {
      ref->error("A definition that has `runs on' clause cannot "
                 "execute testcases");
      goto error;
    }
    if (testcase_inst.timerval) {
      testcase_inst.timerval->chk_expr_float(Type::EXPECTED_DYNAMIC_VALUE);
      Value *t_val = testcase_inst.timerval->get_value_refd_last();
      if (t_val->get_valuetype() == Value::V_REAL) {
        ttcn3float v_real = t_val->get_val_Real();
        if (v_real < 0.0) {
          testcase_inst.timerval->error("The testcase guard "
            "timer has negative duration: `%s'", Real2string(v_real).c_str());
        } else if (isSpecialFloatValue(v_real)) {
          testcase_inst.timerval->error("The testcase guard "
            "timer duration cannot be %s", Real2string(v_real).c_str());
        }
      }
    }
    return;
  error:
    clean_up();
    statementtype=S_ERROR;
  }

  void Statement::chk_execute_refd()
  {
    Error_Context cntxt(this, "In execute statement");
    switch(execute_refd.value->get_valuetype()){
    case Value::V_REFER:
      execute_refd.value->error("A value of a testcase type was expected "
        "in the argument instead of a `refers' statement,"
        " which does not specify any function type");
      return;
    case Value::V_TTCN3_NULL:
      execute_refd.value->error("A value of a testcase type was expected "
        "in the argument instead of a `null' value,"
        " which does not specify any function type");
      return;
    default:
      break;
    }
    Type *t = execute_refd.value->get_expr_governor_last();
    if (!t) goto error;
    switch (t->get_typetype()) {
    case Type::T_ERROR:
      goto error;
    case Type::T_TESTCASE:
      break;
    default:
      execute_refd.value->error("A value of type testcase was expected in the "
        "argument of `derefers()' instead of `%s'", t->get_typename().c_str());
      goto error;
    }
    if (my_sb->get_scope_runs_on()) {
      execute_refd.value->error("A definition that has `runs on' clause cannot "
        "execute testcases");
      goto error;
    } else {
      ActualParList *parlist = new ActualParList;
      Ttcn::FormalParList *fp_list = t->get_fat_parameters();
      bool is_erroneous = fp_list->chk_actual_parlist(execute_refd.t_list1,
	parlist);
      delete execute_refd.t_list1;
      if(is_erroneous) {
	delete parlist;
	execute_refd.ap_list2 = 0;
	goto error;
      }else {
	parlist->set_fullname(get_fullname());
	parlist->set_my_scope(my_sb);
	execute_refd.ap_list2 = parlist;
      }
      if(execute_refd.timerval) {
	execute_refd.timerval->chk_expr_float(Type::EXPECTED_DYNAMIC_VALUE);
	Value *t_val = execute_refd.timerval->get_value_refd_last();
	if(t_val->get_valuetype() == Value::V_REAL) {
	  ttcn3float v_real = t_val->get_val_Real();
          if(v_real < 0.0) {
            execute_refd.value->error("The testcase guard "
              "timer has negative duration: `%s'", Real2string(v_real).c_str());
          } else if (isSpecialFloatValue(v_real)) {
            execute_refd.value->error("The testcase guard "
              "timer duration cannot be %s", Real2string(v_real).c_str());
          }
	}
      }
    }
    return;
error:
    clean_up();
    statementtype=S_ERROR;
  }

  Type *Statement::chk_port_ref(Reference *p_ref, bool p_any_from)
  {
    if (!my_sb->get_my_def())
      error("Port operation is not allowed in the control part");
    if (!p_ref) return 0;
    Common::Assignment *t_ass = p_ref->get_refd_assignment();
    if (!t_ass) return 0;
    switch (t_ass->get_asstype()) {
    case Common::Assignment::A_PORT: {
      ArrayDimensions *t_dims = t_ass->get_Dimensions();
      if (t_dims) t_dims->chk_indices(p_ref, "port", false,
	Type::EXPECTED_DYNAMIC_VALUE, p_any_from);
      else {
        if (p_any_from) {
          p_ref->error("Reference to a port array was expected instead of "
            "a port");
        }
        else if (p_ref->get_subrefs()) p_ref->error("Reference to single %s "
	"cannot have field or array sub-references",
	t_ass->get_description().c_str());
      }
      break; }
    case Common::Assignment::A_PAR_PORT:
      if (p_any_from) {
        p_ref->error("Reference to a port array was expected instead of "
          "a port parameter");
      }
      if (p_ref->get_subrefs()) p_ref->error("Reference to %s cannot have "
        "field or array sub-references", t_ass->get_description().c_str());
      break;
    default:
      p_ref->error("Reference to a port %s was expected instead of %s",
        p_any_from ? "array" : "or port parameter",
        t_ass->get_description().c_str());
      return 0;
    }
    Type *ret_val = t_ass->get_Type();
    if (!ret_val) return 0;
    ret_val = ret_val->get_type_refd_last();
    if (ret_val->get_typetype() == Type::T_PORT) return ret_val;
    else return 0;
  }
  
  void Statement::chk_index_redirect(Reference* p_index_ref,
                                     ArrayDimensions* p_array_dims,
                                     bool p_any_from, const char* p_array_type)
  {
    Error_Context cntxt(p_index_ref, "In index redirect");
    if (!p_any_from) {
      p_index_ref->error("Index redirect cannot be used without the "
        "'any from' clause");
    }
    Type* ref_type = p_index_ref->chk_variable_ref();
    if (ref_type != NULL) {
      size_t nof_dims = p_array_dims != NULL ? p_array_dims->get_nof_dims() : 0;
      Type* ref_type_last = ref_type->get_type_refd_last();
      Type::typetype_t tt = ref_type_last->get_typetype_ttcn3();
      switch (tt) {
      case Type::T_INT:
        if (nof_dims > 1) {
          p_index_ref->error("Indices of multi-dimensional %s arrays can only be "
            "redirected to an integer array or a record of integers",
            p_array_type);
        }
        else if (nof_dims == 1 && ref_type->get_sub_type() != NULL) {
          // make sure all possible indices are allowed by the subtype
          Ttcn::ArrayDimension* dim = p_array_dims->get_dim_byIndex(0);
          for (size_t i = 0; i < dim->get_size(); ++i) {
            Value v(Value::V_INT, dim->get_offset() + static_cast<Int>(i));
            ref_type->get_sub_type()->chk_this_value(&v);
          }
        }
        break;
      case Type::T_ARRAY:
      case Type::T_SEQOF: {
        if (nof_dims == 1) {
          p_index_ref->error("Indices of one-dimensional %s arrays can only be "
            "redirected to an integer", p_array_type);
        }
        else {
          Type* of_type = ref_type_last->get_ofType();
          Type::typetype_t tt_elem = of_type->get_type_refd_last()->get_typetype_ttcn3();
          if (tt_elem == tt) {
            p_index_ref->error("The 'record of' or array in the index redirect "
              "must be one-dimensional");
          }
          else if (tt_elem != Type::T_INT) {
            p_index_ref->error("The element type of %s in an index "
              "redirect must be integer", tt == Type::T_ARRAY ? "an array" :
              "a 'record of'");
          }
          if (nof_dims != 0) {
            boolean error_flag = FALSE;
            if (tt == Type::T_ARRAY) {
              ArrayDimension* dim = ref_type_last->get_dimension();
              if (dim->get_size() != nof_dims) {
                p_index_ref->error("Size of integer array is invalid: the %s array"
                  " has %lu dimensions, but the integer array has %lu element%s",
                  p_array_type, static_cast<unsigned long>(nof_dims), static_cast<unsigned long>(dim->get_size()),
                  dim->get_size() == 1 ? "" : "s");
                error_flag = TRUE;
              }
            }
            else if (nof_dims != 0 && ref_type->get_sub_type() != NULL &&
                     !ref_type->get_sub_type()->length_allowed(nof_dims)) {
              p_index_ref->error("This index redirect would result in a record "
                "of integer of length %lu, which is not allowed by the length "
                "restrictions of type `%s'",
                nof_dims, ref_type->get_typename().c_str());
              error_flag = TRUE;
            }
            if (!error_flag && of_type->get_sub_type() != NULL) {
              // make sure all possible indices are allowed by the element 
              // type's subtype
              for (size_t i = 0; i < nof_dims; ++i) {
                Error_Context context(p_index_ref, "In dimension #%lu",
                  static_cast<unsigned long>(i + 1));
                ArrayDimension* dim = p_array_dims->get_dim_byIndex(i);
                for (size_t j = 0; j < dim->get_size(); ++j) {
                  Value v(Value::V_INT, dim->get_offset() + static_cast<Int>(j));
                  of_type->get_sub_type()->chk_this_value(&v);
                }
              }
            }
          }
        }
        break; }
      default:
        p_index_ref->error("Indices of %s arrays can only be redirected to an "
          "integer, an integer array or a record of integers", p_array_type);
        break;
      }
    }
  }

  void Statement::chk_to_clause(Type *port_type)
  {
    if (!port_op.s.toclause) return;
    // pointer to the address type
    Type *address_type;
    if (port_type) {
      // the port type is known
      address_type = port_type->get_PortBody()->get_address_type();
    } else {
      // the port type is unknown
      // address is permitted if it is visible from the current module
      address_type = my_sb->get_scope_mod()->get_address_type();
    }
    Error_Context cntxt(port_op.s.toclause, "In `to' clause");
    if (address_type) {
      // detect possible enumerated values (address may be an enumerated type)
      address_type->chk_this_value_ref(port_op.s.toclause);
      // try to figure out whether the argument is a component reference or
      // an SUT address
      bool is_address;
      Type *t_governor =
	port_op.s.toclause->get_expr_governor(Type::EXPECTED_DYNAMIC_VALUE);
      if (t_governor) is_address = address_type->is_compatible(t_governor, NULL, this);
      else is_address =
        port_op.s.toclause->get_expr_returntype(Type::EXPECTED_DYNAMIC_VALUE)
	  != Type::T_COMPONENT;
      if (is_address) {
	// the argument is an address value
	port_op.s.toclause->set_my_governor(address_type);
	address_type->chk_this_value(port_op.s.toclause, 0,
	  Type::EXPECTED_DYNAMIC_VALUE, INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED,
	  SUB_CHK);
      } else {
	// the argument is not an address value, treat as a component reference
	chk_comp_ref(port_op.s.toclause, true, true);
      }
    } else {
      // usage of address is not allowed
      chk_comp_ref(port_op.s.toclause, true, true);
    }
  }

  void Statement::chk_from_clause(Type *port_type)
  {
    if (!port_op.r.fromclause && !port_op.r.redirect.sender) return;
    // pointer to the address type
    Type *address_type;
    if (port_type) {
      // the port type is known
      address_type = port_type->get_PortBody()->get_address_type();
    } else if (port_op.portref) {
      // the operation refers to a specific port, but its type is unknown
      // address is permitted if it is visible from the current module
      address_type = my_sb->get_scope_mod()->get_address_type();
    } else {
      // the operation refers to 'any port'
      // address is not allowed
      address_type = 0;
    }
    bool sender_redirect_checked = false;
    Type *from_clause_type = 0;
    if (port_op.r.fromclause) {
      // the from clause is present
      Error_Context cntxt(port_op.r.fromclause, "In `from' clause");
      from_clause_type =
	port_op.r.fromclause->get_expr_governor(Type::EXPECTED_TEMPLATE);
      Template *templ_body = port_op.r.fromclause->get_Template();
      if (!from_clause_type) {
	// try to detect possible enumerated values
	if (address_type) address_type->chk_this_template_ref(templ_body);
	else templ_body->set_lowerid_to_ref();
	from_clause_type =
	  templ_body->get_expr_governor(Type::EXPECTED_TEMPLATE);
      }
      if (!from_clause_type) {
        // trying to determine the type of template in from clause
	// based on the sender redirect
	from_clause_type = chk_sender_redirect(address_type);
	sender_redirect_checked = true;
      }
      if (!from_clause_type) {
	// trying to figure out whether the template is a component reference
	// or an SUT address
        bool is_compref;
	if (templ_body->get_expr_returntype(Type::EXPECTED_TEMPLATE)
	    == Type::T_COMPONENT) is_compref = true;
	else {
	  switch (templ_body->get_templatetype()) {
	  case Template::SPECIFIC_VALUE:
	    // treat 'null' as component reference
	    if (templ_body->get_specific_value()->get_valuetype() ==
		Value::V_TTCN3_NULL) is_compref = true;
	    else is_compref = false;
	    break;
	  case Template::ANY_VALUE:
	  case Template::ANY_OR_OMIT:
	    // treat generic wildcards ? and * as component references
	    is_compref = true;
	    break;
	  default:
	    is_compref = false;
	  }
	}
	if (is_compref) {
	  // the argument is a component reference: get a pool type
	  from_clause_type = Type::get_pooltype(Type::T_COMPONENT);
	} else if (address_type) {
	  // the argument is not a component reference: try the address type
	  from_clause_type = address_type;
	}
      }
      if (from_clause_type) {
	// the type of from clause is known
	port_op.r.fromclause->chk(from_clause_type);
	if (!address_type
	    || !address_type->is_compatible(from_clause_type, NULL, this)) {
	  // from_clause_type must be a component type
	  switch (from_clause_type->get_type_refd_last()->get_typetype()) {
	  case Type::T_ERROR:
	    // to avoid further errors in sender redirect
	    from_clause_type = 0;
	  case Type::T_COMPONENT:
	    if (templ_body->get_templatetype() == Template::SPECIFIC_VALUE)
	      chk_comp_ref(templ_body->get_specific_value(), true, true);
	    break;
	  default:
	    port_op.r.fromclause->error("The type of the template should be a "
	      "component type %sinstead of `%s'",
	      address_type ? "or the `address' type " : "",
	      from_clause_type->get_typename().c_str());
	    // to avoid further errors in sender redirect
	    from_clause_type = 0;
	  }
	}
      } else {
	// the type of from clause is unknown
	port_op.r.fromclause->error("Cannot determine the type of the "
	  "template");
      }
    }
    if (!sender_redirect_checked) {
      Type *sender_redirect_type = chk_sender_redirect(address_type);
      if (from_clause_type && sender_redirect_type &&
          !from_clause_type->is_identical(sender_redirect_type)) {
	error("The types in `from' clause and `sender' redirect are not the "
	  "same: `%s' was expected instead of `%s'",
	  from_clause_type->get_typename().c_str(),
	  sender_redirect_type->get_typename().c_str());
      }
    }
  }

  void Statement::chk_call_body(Type *port_type, Type *signature)
  {
    bool has_catch_timeout = false;
    // setting the flags whether 'catch(timeout)' statements are allowed
    for (size_t i = 0; i < port_op.s.call.body->get_nof_ags(); i++) {
      AltGuard *t_ag = port_op.s.call.body->get_ag_byIndex(i);
      if (t_ag->get_type() != AltGuard::AG_OP)
	FATAL_ERROR("Statement::chk_call_body()");
      Statement *t_stmt = t_ag->get_guard_stmt();
      if (t_stmt->statementtype == S_CATCH) {
	t_stmt->port_op.r.ctch.in_call = true;
	if (port_op.s.call.timer)
	  t_stmt->port_op.r.ctch.call_has_timer = true;
	if (t_stmt->port_op.r.ctch.timeout) has_catch_timeout = true;
      }
    }
    Error_Context cntxt(this, "In response and exception handling part");
    port_op.s.call.body->set_my_laic_stmt(port_op.s.call.body, 0);
    port_op.s.call.body->set_my_ags(port_op.s.call.body);
    port_op.s.call.body->chk();
    if (port_type) {
      // checking whether getreply/catch operations refer to the same port
      // and same signature as the call operation
      for (size_t i = 0; i < port_op.s.call.body->get_nof_ags(); i++) {
	AltGuard *t_ag = port_op.s.call.body->get_ag_byIndex(i);
	if (t_ag->get_type() != AltGuard::AG_OP)
	  FATAL_ERROR("Statement::chk_call_body()");
	Statement *t_stmt = t_ag->get_guard_stmt();
	if (t_stmt->statementtype == S_ERROR) continue;
	// checking port reference
	if (!t_stmt->port_op.portref) {
	  t_stmt->error("The `%s' operation must refer to the same port as "
	    "the previous `call' statement: `%s' was expected instead of "
	    "`any port'", t_stmt->get_stmt_name(),
	    port_op.portref->get_id()->get_dispname().c_str());
	} else if (*port_op.portref->get_id() !=
		   *t_stmt->port_op.portref->get_id()) {
	  t_stmt->port_op.portref->error("The `%s' operation refers to a "
	    "different port than the previous `call' statement: `%s' was "
	    "expected instead of `%s'", t_stmt->get_stmt_name(),
	    port_op.portref->get_id()->get_dispname().c_str(),
	    t_stmt->port_op.portref->get_id()->get_dispname().c_str());
	}
	// checking the signature
	switch (t_stmt->statementtype) {
	case S_GETREPLY:
	  if (t_stmt->port_op.r.rcvpar) {
	    Type *t_sig = t_stmt->port_op.r.rcvpar
	      ->get_expr_governor(Type::EXPECTED_DYNAMIC_VALUE);
	    if (!signature->is_compatible(t_sig, NULL, NULL))
	      t_stmt->port_op.r.rcvpar->error("The `getreply' operation refers "
		"to a different signature than the previous `call' statement: "
		"`%s' was expected instead of `%s'",
		signature->get_typename().c_str(),
		t_sig->get_typename().c_str());
	  }
	  break;
	case S_CATCH:
	  if (t_stmt->port_op.r.ctch.signature
	      && !signature->is_compatible(t_stmt->port_op.r.ctch.signature, NULL, NULL))
	    t_stmt->port_op.r.ctch.signature_ref->error("The `catch' "
	      "operation refers to a different signature than the previous "
	      "`call' statement: `%s' was expected instead of `%s'",
	      signature->get_typename().c_str(),
	      t_stmt->port_op.r.ctch.signature->get_typename().c_str());
	  break;
	default:
	  FATAL_ERROR("Statement::chk_call_body()");
	}
      }
    }
    if (port_op.s.call.timer && !has_catch_timeout)
      warning("The call operation has a timer, but the timeout exception is "
	"not caught");
  }

  Type *Statement::get_msg_sig_type(TemplateInstance *p_ti)
  {
    // first analyze the template instance as is
    Type *ret_val = p_ti->get_expr_governor(Type::EXPECTED_TEMPLATE);
    // return if this step was successful
    if (ret_val) return ret_val;
    // try to convert the undef identifier in the template instance to
    // a reference because it cannot be an enum value anymore
    Template *t_templ = p_ti->get_Template();
    t_templ->set_lowerid_to_ref();
    return t_templ->get_expr_governor(Type::EXPECTED_TEMPLATE);
  }

  Type *Statement::chk_sender_redirect(Type *address_type)
  {
    if (!port_op.r.redirect.sender) return 0;
    Error_Context cntxt(port_op.r.redirect.sender, "In `sender' redirect");
    Type *t_var_type = port_op.r.redirect.sender->chk_variable_ref();
    if (!t_var_type) return 0;
    if (!address_type || !address_type->is_identical(t_var_type)) {
      // t_var_type must be a component type
      switch (t_var_type->get_type_refd_last()->get_typetype()) {
      case Type::T_COMPONENT:
	break;
      case Type::T_ERROR:
        return 0;
      default:
	port_op.r.redirect.sender->error("The type of the variable should be "
	  "a component type %sinstead of `%s'",
	  address_type ? "or the `address' type " : "",
	  t_var_type->get_typename().c_str());
	return 0;
      }
    }
    return t_var_type;
  }
  
  void Statement::chk_timestamp_redirect(Type* port_type, bool outgoing)
  {
    Reference* tr = outgoing ? port_op.s.timestampredirect :
      port_op.r.redirect.timestamp;
    if (tr == NULL) {
      return;
    }
    
    if (port_type != NULL && !port_type->get_PortBody()->is_realtime()) {
      tr->error("The timestamp cannot be redirected, because port type `%s' "
        "does not have the 'realtime' clause", port_type->get_typename().c_str());
    }
    
    Type* t_var_type = tr->chk_variable_ref();
    if (t_var_type != NULL &&
        t_var_type->get_type_refd_last()->get_typetype() != Type::T_REAL) {
      tr->error("The type of the variable should be float instead of `%s'",
        t_var_type->get_typename().c_str());
    }
  }

  Type *Statement::chk_signature_ref(Reference *p_ref)
  {
    if (!p_ref) FATAL_ERROR("Statement::chk_signature_ref()");
    Error_Context cntxt(p_ref, "In signature");
    Common::Assignment *t_ass = p_ref->get_refd_assignment();
    if (!t_ass) return 0;
    if (t_ass->get_asstype() != Common::Assignment::A_TYPE) {
      p_ref->error("Reference to a signature was expected instead of %s",
	t_ass->get_description().c_str());
      return 0;
    }
    Type *ret_val = t_ass->get_Type();
    if (!ret_val) return 0;
    ret_val = ret_val->get_field_type(p_ref->get_subrefs(),
      Type::EXPECTED_DYNAMIC_VALUE);
    if (!ret_val) return 0;
    ret_val = ret_val->get_type_refd_last();
    switch (ret_val->get_typetype()) {
    case Type::T_SIGNATURE:
      break;
    case Type::T_ERROR:
      return 0;
    case Type::T_PORT:
      p_ref->error("Reference to a signature was expected instead of port type "
	"`%s'", ret_val->get_typename().c_str());
      return 0;
    default:
      p_ref->error("Reference to a signature was expected instead of data type "
	"`%s'", ret_val->get_typename().c_str());
      return 0;
    }
    return ret_val;
  }

  void Statement::chk_timer_ref(Reference *p_ref, bool p_any_from)
  {
    if (!p_ref) return;
    Common::Assignment *t_ass = p_ref->get_refd_assignment();
    if (!t_ass) return;
    switch (t_ass->get_asstype()) {
    case Common::Assignment::A_TIMER: {
      ArrayDimensions *t_dims = t_ass->get_Dimensions();
      if (t_dims) t_dims->chk_indices(p_ref, "timer", false,
        Type::EXPECTED_DYNAMIC_VALUE, p_any_from);
      else {
        if (p_any_from) {
          p_ref->error("Reference to a timer array was expected instead of "
            "a timer");
        }
        else if (p_ref->get_subrefs()) {
          p_ref->error("Reference to single %s cannot have field or array "
            "sub-references", t_ass->get_description().c_str());
        }
      }
      break; }
    case Common::Assignment::A_PAR_TIMER:
      if (p_any_from) {
        p_ref->error("Reference to a timer array was expected instead of "
          "a timer parameter");
      }
      if (p_ref->get_subrefs()) p_ref->error("Reference to %s cannot have "
	"field or array sub-references", t_ass->get_description().c_str());
      break;
    default:
      p_ref->error("Reference to a timer %s was expected instead of %s",
        p_any_from ? "array" : "or timer parameter",
        t_ass->get_description().c_str());
    }
  }

  Type *Statement::chk_comp_ref(Value *p_val, bool allow_mtc, bool allow_system,
                                bool p_any_from)
  {
    if (!my_sb->get_my_def())
      error("Component operation is not allowed in the control part");
    if (!p_val) return 0;
    Value *v = p_val->get_value_refd_last();
    switch (v->get_valuetype()) {
    case Value::V_ERROR:
      return 0;
    case Value::V_REFD:
    case Value::V_INVOKE:
      break;
    case Value::V_TTCN3_NULL:
      p_val->error("The `null' component reference shall not be used in `%s' operation", get_stmt_name());
      break;
    case Value::V_EXPR:
      switch (v->get_optype()) {
      case Value::OPTYPE_COMP_NULL:
	p_val->error("The `null' component reference shall not be used in `%s' operation", get_stmt_name());
        break;
      case Value::OPTYPE_COMP_MTC:
	if (!allow_mtc)
	  p_val->error("The `mtc' component reference shall not be used in `%s' operation", get_stmt_name());
        break;
      case Value::OPTYPE_COMP_SYSTEM:
	if (!allow_system)
	  p_val->error("The `system' component reference shall not be used in `%s' operation", get_stmt_name());
        break;
      case Value::OPTYPE_COMP_SELF:
      case Value::OPTYPE_COMP_CREATE:
        break;
      default:
	p_val->error("A component reference was expected as operand");
	return 0;
      }
      break;
    default:
      p_val->error("A component reference was expected as operand");
      return 0;
    }
    Type *ret_val = p_val->get_expr_governor(Type::EXPECTED_DYNAMIC_VALUE);
    if (!ret_val) return 0;
    ret_val = ret_val->get_type_refd_last();
    switch (ret_val->get_typetype()) {
    case Type::T_ERROR:
      return 0;
    case Type::T_COMPONENT:
      if (!p_any_from) {
        return ret_val;
      }
      break;
    case Type::T_ARRAY:
      if (p_any_from) {
        Type* of_type = ret_val->get_ofType()->get_type_refd_last();
        while (of_type->get_typetype() == Type::T_ARRAY) {
          of_type = of_type->get_ofType()->get_type_refd_last();
        }
        if (of_type->get_typetype() == Type::T_COMPONENT) {
          return ret_val;
        }
      }
      break;
    case Type::T_CHOICE_T: 
      if (v->is_ref()) {
        CompField* def_alt = ret_val->get_default_alternative();
        Reference* ttcn_ref = dynamic_cast<Reference*>(v->get_reference());
        if (def_alt != NULL && ttcn_ref != NULL) {
          Error_Context cntxt(v, "Using default alternative `%s' in value of union type `%s'",
            def_alt->get_name().get_dispname().c_str(), ret_val->get_typename().c_str());
          ttcn_ref->use_default_alternative(def_alt->get_name());
          return chk_comp_ref(p_val, allow_mtc, allow_system, p_any_from);
        }
      }
      break;
    default:
      break;
    }
    p_val->error("Type mismatch: The type of the operand should be a "
      "component%s type instead of `%s'", p_any_from ? " array" : "",
      ret_val->get_typename().c_str());
    return 0;
  }

  Type *Statement::chk_conn_endpoint(Value *p_compref, Reference *p_portref,
    bool allow_system)
  {
    Type *comp_type = chk_comp_ref(p_compref, true, allow_system);
    if (comp_type) {
      ComponentTypeBody *comp_body = comp_type->get_CompBody();
      p_portref->set_base_scope(comp_body);
      const Identifier& t_portid = *p_portref->get_id();
      if (!comp_body->has_local_ass_withId(t_portid)) {
        p_portref->error("Component type `%s' does not have port with name "
          "`%s'", comp_type->get_typename().c_str(),
          t_portid.get_dispname().c_str());
        return 0;
      }
      Common::Assignment *t_ass = comp_body->get_local_ass_byId(t_portid);
      if (t_ass->get_asstype() != Common::Assignment::A_PORT) {
        p_portref->error("Definition `%s' in component type `%s' is a %s and "
          "not a port", t_portid.get_dispname().c_str(),
          comp_type->get_typename().c_str(), t_ass->get_assname());
        return 0;
      }
      ArrayDimensions *t_dims = t_ass->get_Dimensions();
      if (t_dims) t_dims->chk_indices(p_portref, "port", false,
        Type::EXPECTED_DYNAMIC_VALUE);
      else if (p_portref->get_subrefs()) {
        p_portref->error("Port `%s' is not an array. The "
          "reference cannot have array indices",
          t_portid.get_dispname().c_str());
      }
      Type *port_type = t_ass->get_Type();
      if (port_type) {
        // check whether the external interface is provided by another port type
        PortTypeBody *port_body = port_type->get_PortBody();
        if (port_body->get_type() == PortTypeBody::PT_USER && port_body->is_legacy()) {
          Type *provider_type = port_body->get_provider_type();
          if (provider_type) port_type = provider_type;
        }
      }
      return port_type;
    } else {
      // the component type cannot be determined
      FieldOrArrayRefs *t_subrefs = p_portref->get_subrefs();
      if (t_subrefs) {
        // check the array indices: they should be integers
        for (size_t i = 0; i < t_subrefs->get_nof_refs(); i++) {
          t_subrefs->get_ref(i)->get_val()
            ->chk_expr_int(Type::EXPECTED_DYNAMIC_VALUE);
        }
      }
      return 0;
    }
  }
  
  void Statement::chk_update()
  {
    Error_Context cntxt(this, "In @update statement");
    if (!use_runtime_2) {
      error("The @update statement is only available in the Function Test "
        "runtime");
      return;
    }
    if (my_sb->is_in_dynamic_template()) {
      error("The @update statement is not allowed in the statement block of a dynamic template");
      return;
    }
    Common::Assignment* refd_ass = update_op.ref->get_refd_assignment(false);
    if (refd_ass != NULL) {
      switch (refd_ass->get_asstype()) {
      case Definition::A_CONST:
      case Definition::A_TEMPLATE:
        break; // OK
      default:
        update_op.ref->error("Reference to constant or template definition was "
          "expected instead of %s", refd_ass->get_assname());
        return;
      }
      Type* ref_type = refd_ass->get_Type();
      if (ref_type != NULL &&
          !ErroneousDescriptors::can_have_err_attribs(ref_type)) {
        update_op.ref->error("Type `%s' cannot have erroneous attributes",
          ref_type->get_typename().c_str());
      }
      if (update_op.w_attrib_path != NULL) {
        update_op.w_attrib_path->chk_only_erroneous();
        update_op.err_attrib = Definition::chk_erroneous_attr(update_op.w_attrib_path,
          ref_type, my_sb, get_fullname(), true);
        if (update_op.err_attrib != NULL) {
          GovernedSimple* refd_obj = static_cast<GovernedSimple*>(
            refd_ass->get_Setting());
          refd_obj->add_err_descr(this, update_op.err_attrib->get_err_descr());
        }
      }
    }
    if (update_op.ref->get_subrefs() != NULL) {
      update_op.ref->error("Field names and array indexes are not allowed in "
        "this context");
    }
  }
  
  void Statement::chk_setstate() {
    Error_Context cntxt(this, "In setstate statement");
    {
      Error_Context cntxt2(this, "In first parameter");
      setstate_op.val->chk_expr_int(Type::EXPECTED_DYNAMIC_VALUE);
      if (!setstate_op.val->is_unfoldable()) {
        bool error = false;
        if (setstate_op.val->get_val_Int()->is_native()) {
          switch(setstate_op.val->get_val_Int()->get_val()) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
              break;
            default:
              error = true;
          }
        } else {
          error = true;
        }
        if (error) {
          setstate_op.val->error("The value of the first parameter must be 0, 1, 2, 3 or 4.");
        }
      }
    }
    if (setstate_op.ti != NULL) {
      Error_Context cntxt2(this, "In second parameter");
      Type* t = setstate_op.ti->get_expr_governor(Type::EXPECTED_DYNAMIC_VALUE);
      if (t != NULL) {
        setstate_op.ti->chk(t);
      } else {
        setstate_op.ti->error("Cannot determine the type of the parameter.");
      }
    }
  }
  
  void Statement::chk_setencode()
  {
    if (legacy_codec_handling) {
      FATAL_ERROR("Statement::chk_setencode");
    }
    Error_Context cntxt(this, "In setencode statement");
    Common::Type* type = setencode_op.type;
    type->chk();
    Type* t_ct = type->get_type_w_coding_table();
    bool type_error = (type->get_typetype() == Common::Type::T_ERROR);
    if (!type_error) {
      if (t_ct == NULL) {
        type->error("The type argument has no encoding rules defined");
        type_error = true;
      }
      else if (t_ct->get_coding_table().size() == 1) {
        type->warning("The type argument has only one encoding rule defined. "
          "The 'setencode' statement will be ignored");
      }
    }
    {
      Common::Value* enc_str = setencode_op.encoding;
      Error_Context cntxt2(enc_str, "In the second argument");
      enc_str->set_lowerid_to_ref();
      Common::Type::get_pooltype(Type::T_USTR)->chk_this_value(enc_str, NULL,
        Common::Type::EXPECTED_DYNAMIC_VALUE, INCOMPLETE_NOT_ALLOWED,
        OMIT_NOT_ALLOWED, NO_SUB_CHK);
      if (!type_error && enc_str->get_valuetype() != Common::Value::V_ERROR &&
          !enc_str->is_unfoldable()) {
        enc_str->chk_dyn_enc_str(type);
      }
    }
    RunsOnScope* runs_on_scope = my_sb->get_scope_runs_on();
    if (runs_on_scope == NULL) {
      error("'self.setencode' must be in a definition with a runs-on clause");
    }
    else if (!type_error && t_ct->get_coding_table().size() >= 2) {
      runs_on_scope->get_component_type()->get_CompBody()->
        add_default_coding_var(type);
    }
  }
  
  void Statement::chk_raise_oop()
  {
    Error_Context cntxt(this, "In raise statement");
    if (!oop_features) {
      error("The compiler option `-k' must be activated to use object-oriented features such as raise statements.");
    }
    Definition *my_def = my_sb->get_my_def();
    if (!my_def) {
      error("Raise statement cannot be used in the control part. "
            "It is allowed only in functions, altsteps and testcases.");
    }
    if (my_sb->is_in_finally_block()) {
      error("Raise statement cannot be used inside a finally block or the destructor of a class.");
    }
    Common::Type* exc_type = raise_op.ti->get_expr_governor(Common::Type::EXPECTED_DYNAMIC_VALUE);
    if (exc_type == NULL) {
      raise_op.ti->get_Template()->set_lowerid_to_ref();
      exc_type = raise_op.ti->get_expr_governor(Common::Type::EXPECTED_DYNAMIC_VALUE);
    }
    if (exc_type == NULL) {
      if (raise_op.ti->get_Template()->get_templatetype() != Template::TEMPLATE_ERROR &&
          (raise_op.ti->get_Template()->get_templatetype() != Template::SPECIFIC_VALUE ||
           raise_op.ti->get_Template()->get_specific_value()->get_valuetype() != Common::Value::V_ERROR)) {
        error("Cannot determine the type of the raised exception.");
      }
      exc_type = Type::get_pooltype(Type::T_ERROR);
    }
    switch (exc_type->get_type_refd_last()->get_typetype()) {
    case Common::Type::T_DEFAULT:
    case Common::Type::T_SIGNATURE:
    //case Common::Type::T_PORT: // this case is handled by Template::get_expr_governor
      error("Cannot raise an exception of type %s", exc_type->get_typename().c_str());
      break;
    default:
      break;
    }
    if (raise_op.ti->get_Type() == exc_type) {
      // don't use the type indicator of the TemplateInstance
      // (i.e. the 'type:' part of the in-line template)
      // as the exception's governor, because it will be deleted
      if (exc_type->is_ref()) {
        exc_type = exc_type->get_type_refd();
      }
      else {
        exc_type = Type::get_pooltype(exc_type->get_typetype());
      }
    }
    raise_op.ti->chk(exc_type);
    if (!raise_op.ti->get_Template()->is_Value()) {
      error("A specific value without matching symbols or a reference to a value was expected for a raised exception.");
      delete raise_op.ti;
      raise_op.v = new Common::Value(Common::Value::V_ERROR);
    }
    else {
      Common::Value* val = raise_op.ti->get_Template()->get_Value();
      delete raise_op.ti;
      raise_op.v = val;
    }
    raise_op.checked = true;
  }

  void Statement::set_code_section(
    GovernedSimple::code_section_t p_code_section)
  {
    switch(statementtype) {
    case S_ERROR:
    case S_DEF:
    case S_LABEL:
    case S_GOTO:
    case S_BREAK:
    case S_CONTINUE:
    case S_STOP_EXEC:
    case S_REPEAT:
    case S_START_PROFILER:
    case S_STOP_PROFILER:
      break;
    case S_ASSIGNMENT:
      ass->set_code_section(p_code_section);
      break;
    case S_FUNCTION_INSTANCE:
    case S_ALTSTEP_INSTANCE:
    case S_ACTIVATE:
      ref_pard->set_code_section(p_code_section);
      break;
    case S_BLOCK:
      block->set_code_section(p_code_section);
      break;
    case S_LOG:
    case S_ACTION:
    case S_STOP_TESTCASE:
      if (logargs) logargs->set_code_section(p_code_section);
      break;
    case S_IF:
      if_stmt.ics->set_code_section(p_code_section);
      if (if_stmt.elseblock)
        if_stmt.elseblock->set_code_section(p_code_section);
      break;
    case S_FOR:
      if (!loop.for_stmt.varinst)
        loop.for_stmt.init_ass->set_code_section(p_code_section);
      loop.for_stmt.finalexpr->set_code_section(p_code_section);
      loop.for_stmt.step->set_code_section(p_code_section);
      loop.block->set_code_section(p_code_section);
      break;
    case S_WHILE:
    case S_DOWHILE:
      loop.expr->set_code_section(p_code_section);
      loop.block->set_code_section(p_code_section);
      break;
    case S_SELECT:
      select.expr->set_code_section(p_code_section);
      select.scs->set_code_section(p_code_section);
      break;
    case S_SELECTUNION:
      select_union.expr->set_code_section(p_code_section);
      select_union.sus->set_code_section(p_code_section);
      break;
    case S_SELECT_CLASS:
      select_class.ref->set_code_section(p_code_section);
      select_class.sccs->set_code_section(p_code_section);
      break;
    case S_ALT:
    case S_INTERLEAVE:
      ags->set_code_section(p_code_section);
      break;
    case S_RETURN:
      if (returnexpr.v) returnexpr.v->set_code_section(p_code_section);
      if (returnexpr.t) returnexpr.t->set_code_section(p_code_section);
      break;
    case S_DEACTIVATE:
      if (deactivate) deactivate->set_code_section(p_code_section);
      break;
    case S_SEND:
      if (!port_op.translate) port_op.portref->set_code_section(p_code_section);
      port_op.s.sendpar->set_code_section(p_code_section);
      if (port_op.s.toclause)
        port_op.s.toclause->set_code_section(p_code_section);
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_code_section(p_code_section);
      }
      break;
    case S_CALL:
      port_op.portref->set_code_section(p_code_section);
      port_op.s.sendpar->set_code_section(p_code_section);
      if (port_op.s.toclause)
        port_op.s.toclause->set_code_section(p_code_section);
      if (port_op.s.call.timer)
        port_op.s.call.timer->set_code_section(p_code_section);
      if (port_op.s.call.body)
        port_op.s.call.body->set_code_section(p_code_section);
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_code_section(p_code_section);
      }
      break;
    case S_REPLY:
      port_op.portref->set_code_section(p_code_section);
      port_op.s.sendpar->set_code_section(p_code_section);
      if (port_op.s.replyval)
        port_op.s.replyval->set_code_section(p_code_section);
      if (port_op.s.toclause)
        port_op.s.toclause->set_code_section(p_code_section);
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_code_section(p_code_section);
      }
      break;
    case S_RAISE:
      port_op.portref->set_code_section(p_code_section);
      port_op.s.sendpar->set_code_section(p_code_section);
      if (port_op.s.toclause)
        port_op.s.toclause->set_code_section(p_code_section);
      if (port_op.s.timestampredirect != NULL) {
        port_op.s.timestampredirect->set_code_section(p_code_section);
      }
      break;
    case S_RECEIVE:
    case S_CHECK_RECEIVE:
    case S_TRIGGER:
      if (port_op.portref) port_op.portref->set_code_section(p_code_section);
      if (port_op.r.rcvpar) port_op.r.rcvpar->set_code_section(p_code_section);
      if (port_op.r.fromclause)
        port_op.r.fromclause->set_code_section(p_code_section);
      if (port_op.r.redirect.value)
        port_op.r.redirect.value->set_code_section(p_code_section);
      if (port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_code_section(p_code_section);
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_code_section(p_code_section);
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_code_section(p_code_section);
      }
      break;
    case S_GETCALL:
    case S_CHECK_GETCALL:
      if (port_op.portref) port_op.portref->set_code_section(p_code_section);
      if (port_op.r.rcvpar) port_op.r.rcvpar->set_code_section(p_code_section);
      if (port_op.r.fromclause)
        port_op.r.fromclause->set_code_section(p_code_section);
      if (port_op.r.redirect.param)
        port_op.r.redirect.param->set_code_section(p_code_section);
      if (port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_code_section(p_code_section);
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_code_section(p_code_section);
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_code_section(p_code_section);
      }
      break;
    case S_GETREPLY:
    case S_CHECK_GETREPLY:
      if (port_op.portref) port_op.portref->set_code_section(p_code_section);
      if (port_op.r.rcvpar) port_op.r.rcvpar->set_code_section(p_code_section);
      if (port_op.r.getreply_valuematch)
        port_op.r.getreply_valuematch->set_code_section(p_code_section);
      if (port_op.r.fromclause)
        port_op.r.fromclause->set_code_section(p_code_section);
      if (port_op.r.redirect.value)
        port_op.r.redirect.value->set_code_section(p_code_section);
      if (port_op.r.redirect.param)
        port_op.r.redirect.param->set_code_section(p_code_section);
      if (port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_code_section(p_code_section);
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_code_section(p_code_section);
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_code_section(p_code_section);
      }
      break;
    case S_CATCH:
    case S_CHECK_CATCH:
      if (port_op.portref) port_op.portref->set_code_section(p_code_section);
      if (port_op.r.rcvpar) port_op.r.rcvpar->set_code_section(p_code_section);
      if (port_op.r.fromclause)
        port_op.r.fromclause->set_code_section(p_code_section);
      if (port_op.r.redirect.value)
        port_op.r.redirect.value->set_code_section(p_code_section);
      if (port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_code_section(p_code_section);
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_code_section(p_code_section);
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_code_section(p_code_section);
      }
      break;
    case S_CHECK:
      if (port_op.portref) port_op.portref->set_code_section(p_code_section);
      if (port_op.r.fromclause)
        port_op.r.fromclause->set_code_section(p_code_section);
      if (port_op.r.redirect.sender)
        port_op.r.redirect.sender->set_code_section(p_code_section);
      if (port_op.r.redirect.index != NULL) {
        port_op.r.redirect.index->set_code_section(p_code_section);
      }
      if (port_op.r.redirect.timestamp != NULL) {
        port_op.r.redirect.timestamp->set_code_section(p_code_section);
      }
      break;
    case S_CLEAR:
    case S_START_PORT:
    case S_STOP_PORT:
    case S_HALT:
      if (port_op.portref) port_op.portref->set_code_section(p_code_section);
      break;
    case S_START_COMP:
      comp_op.compref->set_code_section(p_code_section);
      comp_op.funcinstref->set_code_section(p_code_section);
      break;
    case S_START_COMP_REFD:
      comp_op.compref->set_code_section(p_code_section);
      comp_op.derefered.value->set_code_section(p_code_section);
      break;
    case S_STOP_COMP:
    case S_KILL:
      if (comp_op.compref) comp_op.compref->set_code_section(p_code_section);
      break;
    case S_KILLED:
      if (comp_op.compref) comp_op.compref->set_code_section(p_code_section);
      if (comp_op.index_redirect != NULL) {
        comp_op.index_redirect->set_code_section(p_code_section);
      }
      break;
    case S_DONE:
      if (comp_op.compref) {
        comp_op.compref->set_code_section(p_code_section);
        if (comp_op.donereturn.donematch)
          comp_op.donereturn.donematch->set_code_section(p_code_section);
        if (comp_op.donereturn.redirect)
          comp_op.donereturn.redirect->set_code_section(p_code_section);
        if (comp_op.index_redirect != NULL) {
          comp_op.index_redirect->set_code_section(p_code_section);
        }
      }
      break;
    case S_CONNECT:
    case S_MAP:
    case S_DISCONNECT:
    case S_UNMAP:
      config_op.compref1->set_code_section(p_code_section);
      config_op.portref1->set_code_section(p_code_section);
      config_op.compref2->set_code_section(p_code_section);
      config_op.portref2->set_code_section(p_code_section);
      break;
    case S_START_TIMER:
      timer_op.timerref->set_code_section(p_code_section);
      if (timer_op.value) timer_op.value->set_code_section(p_code_section);
      break;
    case S_TIMEOUT:
      if (timer_op.index_redirect != NULL) {
        timer_op.index_redirect->set_code_section(p_code_section);
      }
      // no break
    case S_STOP_TIMER:
      if (timer_op.timerref)
        timer_op.timerref->set_code_section(p_code_section);
      break;
    case S_SETVERDICT:
      setverdict.verdictval->set_code_section(p_code_section);
      if (setverdict.logargs)
        setverdict.logargs->set_code_section(p_code_section);
      break;
    case S_TESTCASE_INSTANCE:
      testcase_inst.tcref->set_code_section(p_code_section);
      if (testcase_inst.timerval)
        testcase_inst.timerval->set_code_section(p_code_section);
      break;
    case S_TESTCASE_INSTANCE_REFD:
      execute_refd.value->set_code_section(p_code_section);
      if(execute_refd.timerval)
        execute_refd.timerval->set_code_section(p_code_section);
      break;
    case S_ACTIVATE_REFD:
    case S_FUNCTION_INVOKED:
    case S_ALTSTEP_INVOKED:
      fau_refd.value->set_code_section(p_code_section);
      if(fau_refd.ap_list2)
        for(size_t i = 0; i < fau_refd.ap_list2->get_nof_pars(); i++)
            fau_refd.ap_list2->get_par(i)->set_code_section(p_code_section);
      break;
    case S_STRING2TTCN:
    case S_INT2ENUM:
      convert_op.val->set_code_section(p_code_section);
      convert_op.ref->set_code_section(p_code_section);
      break;
    case S_UPDATE:
      update_op.ref->set_code_section(p_code_section);
      break;
    case S_SETSTATE:
      setstate_op.val->set_code_section(p_code_section);
      if (setstate_op.ti != NULL) {
        setstate_op.ti->set_code_section(p_code_section);
      }
      break;
    case S_SETENCODE:
      setencode_op.encoding->set_code_section(p_code_section);
      break;
    case S_RAISE_OOP:
      if (!raise_op.checked) {
        FATAL_ERROR("Statement::set_code_section()");
      }
      raise_op.v->set_code_section(p_code_section);
      break;
    default:
      FATAL_ERROR("Statement::set_code_section()");
    } // switch statementtype
  }

  char *Statement::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    switch (statementtype) {
    case S_BLOCK:
    case S_IF:
    case S_SELECT:
    case S_SELECTUNION:
    case S_SELECT_CLASS:
    case S_FOR:
    case S_WHILE:
    case S_DOWHILE:
      // conditional and loop statements do not need single location setting
      // the embedded expressions, statements have their own locations
      break;
    case S_DEF:
      if (def->get_asstype() == Common::Assignment::A_EXCEPTION) {
        break;
      }
      // else fall through
    default:
      str = update_location_object(str);
    }

    switch(statementtype) {
    case S_DEF:
      str=def->generate_code_str(str);
      break;
    case S_ASSIGNMENT:
      str=ass->generate_code(str);
      break;
    case S_FUNCTION_INSTANCE:
    case S_ALTSTEP_INSTANCE:
      str=generate_code_funcinst(str);
      break;
    case S_FUNCTION_INVOKED:
    case S_ALTSTEP_INVOKED:
      str=generate_code_invoke(str);
      break;
    case S_BLOCK:
      str=generate_code_block(str, def_glob_vars, src_glob_vars);
      break;
    case S_LOG:
      str=generate_code_log(str);
      break;
    case S_LABEL:
      str = generate_code_label(str);
      break;
    case S_GOTO:
      str = generate_code_goto(str);
      break;
    case S_IF:
      str=generate_code_if(str, def_glob_vars, src_glob_vars);
      break;
    case S_SELECT:
      str=generate_code_select(str, def_glob_vars, src_glob_vars);
      break;
    case S_SELECTUNION:
      str=generate_code_select_union(str, def_glob_vars, src_glob_vars);
      break;
    case S_SELECT_CLASS:
      str = generate_code_select_class(str, def_glob_vars, src_glob_vars);
      break;
    case S_FOR:
      str=generate_code_for(str, def_glob_vars, src_glob_vars);
      break;
    case S_WHILE:
      str=generate_code_while(str, def_glob_vars, src_glob_vars);
      break;
    case S_DOWHILE:
      str=generate_code_dowhile(str, def_glob_vars, src_glob_vars);
      break;
    case S_BREAK:
      str=generate_code_break(str);
      break;
    case S_CONTINUE:
      str=generate_code_continue(str);
      break;
    case S_STOP_EXEC:
      str=mputstr(str, "TTCN_Runtime::stop_execution();\n");
      break;
    case S_STOP_TESTCASE:
      str=generate_code_testcase_stop(str);
      break;
    case S_ALT:
      str=ags->generate_code_alt(str, def_glob_vars, src_glob_vars, *this);
      break;
    case S_REPEAT:
      str=generate_code_repeat(str);
      break;
    case S_INTERLEAVE:
      str=generate_code_interleave(str, def_glob_vars, src_glob_vars);
      break;
    case S_RETURN:
      str=generate_code_return(str);
      break;
    case S_ACTIVATE:
      str=generate_code_activate(str);
      break;
    case S_ACTIVATE_REFD:
      str=generate_code_activate_refd(str);
      break;
    case S_DEACTIVATE:
      str=generate_code_deactivate(str);
      break;
    case S_SEND:
      str = generate_code_send(str);
      break;
    case S_CALL:
      str = generate_code_call(str, def_glob_vars, src_glob_vars);
      break;
    case S_REPLY:
      str = generate_code_reply(str);
      break;
    case S_RAISE:
      str = generate_code_raise(str);
      break;
    case S_RECEIVE:
    case S_TRIGGER:
    case S_GETCALL:
    case S_GETREPLY:
    case S_CATCH:
    case S_CHECK:
    case S_CHECK_RECEIVE:
    case S_CHECK_GETCALL:
    case S_CHECK_GETREPLY:
    case S_CHECK_CATCH:
    case S_TIMEOUT:
    case S_DONE:
    case S_KILLED:
      str = generate_code_standalone(str);
      break;
    case S_CLEAR:
      str=generate_code_portop(str, "clear");
      break;
    case S_START_PORT:
      str=generate_code_portop(str, "start");
      break;
    case S_STOP_PORT:
      str=generate_code_portop(str, "stop");
      break;
    case S_HALT:
      str=generate_code_portop(str, "halt");
      break;
    case S_START_COMP:
      str=generate_code_startcomp(str);
      break;
    case S_START_COMP_REFD:
      str=generate_code_startcomp_refd(str);
      break;
    case S_STOP_COMP:
      str = generate_code_compop(str, "stop");
      break;
    case S_KILL:
      str = generate_code_compop(str, "kill");
      break;
    case S_CONNECT:
      str = generate_code_configop(str, "connect");
      break;
    case S_MAP:
      str = generate_code_configop(str, "map");
      break;
    case S_DISCONNECT:
      str = generate_code_configop(str, "disconnect");
      break;
    case S_UNMAP:
      str = generate_code_configop(str, "unmap");
      break;
    case S_START_TIMER:
      str=generate_code_starttimer(str);
      break;
    case S_STOP_TIMER:
      str=generate_code_stoptimer(str);
      break;
    case S_SETVERDICT:
      str=generate_code_setverdict(str);
      break;
    case S_ACTION:
      str=generate_code_action(str);
      break;
    case S_TESTCASE_INSTANCE:
      str=generate_code_testcaseinst(str);
      break;
    case S_TESTCASE_INSTANCE_REFD:
      str=generate_code_execute_refd(str);
      break;
    case S_STRING2TTCN:
      str=generate_code_string2ttcn(str);
      break;
    case S_INT2ENUM:
      str = generate_code_int2enum(str);
      break;
    case S_START_PROFILER:
      str = mputstr(str, "ttcn3_prof.start();\n");
      break;
    case S_STOP_PROFILER:
      str = mputstr(str, "ttcn3_prof.stop();\n");
      break;
    case S_UPDATE:
      str = generate_code_update(str, def_glob_vars, src_glob_vars);
      break;
    case S_SETSTATE:
      str = generate_code_setstate(str);
      break;
    case S_SETENCODE:
      str = generate_code_setencode(str);
      break;
    case S_RAISE_OOP:
      str = generate_code_raise_oop(str);
      break;
    default:
      FATAL_ERROR("Statement::generate_code()");
    } // switch
    return str;
  }

  char* Statement::generate_code_string2ttcn(char *str)
  {
    expression_struct val_expr;
    Code::init_expr(&val_expr);
    convert_op.val->generate_code_expr(&val_expr);

    expression_struct ref_expr;
    Code::init_expr(&ref_expr);
    convert_op.ref->generate_code(&ref_expr);

    str = mputstr(str, val_expr.preamble);
    str = mputstr(str, ref_expr.preamble);

    str = mputprintf(str, "string_to_ttcn(%s,%s);\n", val_expr.expr, ref_expr.expr);

    str = mputstr(str, val_expr.postamble);
    str = mputstr(str, ref_expr.postamble);

    Code::free_expr(&val_expr);
    Code::free_expr(&ref_expr);

    return str;
  }
  
  char* Statement::generate_code_int2enum(char* str)
  {
    expression_struct val_expr;
    Code::init_expr(&val_expr);
    convert_op.val->generate_code_expr(&val_expr);

    expression_struct ref_expr;
    Code::init_expr(&ref_expr);
    convert_op.ref->generate_code(&ref_expr);
    
    // check if the reference is an optional field
    bool is_optional = false;
    FieldOrArrayRefs* subrefs = convert_op.ref->get_subrefs();
    if (NULL != subrefs) {
      Type* ref_type = convert_op.ref->get_refd_assignment()->get_Type()->get_type_refd_last();
      for (size_t i = 0; i < subrefs->get_nof_refs(); ++i) {
        FieldOrArrayRef* subref = subrefs->get_ref(i);
        if (FieldOrArrayRef::ARRAY_REF == subref->get_type()) {
          ref_type = ref_type->get_ofType()->get_type_refd_last();
        }
        else { // FIELD_REF
          CompField* cf = ref_type->get_comp_byName(*subref->get_id());
          if (i == subrefs->get_nof_refs() - 1 && cf->get_is_optional()) {
            is_optional = true;
          }
          ref_type = cf->get_type()->get_type_refd_last();
        }
      }
    }

    str = mputstr(str, val_expr.preamble);
    str = mputstr(str, ref_expr.preamble);

    str = mputprintf(str, "%s%s.int2enum(%s);\n", ref_expr.expr,
      is_optional ? "()" : "", val_expr.expr);

    str = mputstr(str, val_expr.postamble);
    str = mputstr(str, ref_expr.postamble);

    Code::free_expr(&val_expr);
    Code::free_expr(&ref_expr);

    return str;
  }
  
  void Statement::generate_code_expr(expression_struct *expr)
  {
    switch (statementtype) {
    case S_RECEIVE:
      generate_code_expr_receive(expr, "receive");
      break;
    case S_TRIGGER:
      generate_code_expr_receive(expr, "trigger");
      break;
    case S_CHECK_RECEIVE:
      generate_code_expr_receive(expr, "check_receive");
      break;
    case S_GETCALL:
      generate_code_expr_getcall(expr, "getcall");
      break;
    case S_CHECK_GETCALL:
      generate_code_expr_getcall(expr, "check_getcall");
      break;
    case S_GETREPLY:
      generate_code_expr_getreply(expr, "getreply");
      break;
    case S_CHECK_GETREPLY:
      generate_code_expr_getreply(expr, "check_getreply");
      break;
    case S_CATCH:
    case S_CHECK_CATCH:
      generate_code_expr_catch(expr);
      break;
    case S_CHECK:
      generate_code_expr_check(expr);
      break;
    case S_DONE:
      generate_code_expr_done(expr);
      break;
    case S_KILLED:
      generate_code_expr_killed(expr);
      break;
    case S_TIMEOUT:
      generate_code_expr_timeout(expr);
      break;
    default:
      FATAL_ERROR("Statement::generate_code_expr()");
    } //switch
  }

  void Statement::ilt_generate_code(ILT *ilt)
  {
    switch (statementtype) {
    case S_INTERLEAVE:
      ilt_generate_code_interleave(ilt);
      return;
    case S_ALT:
      ilt_generate_code_alt(ilt);
      return;
    case S_DEF:
      ilt_generate_code_def(ilt);
      return;
    default:
      break;
    } // switch
    if (is_receiving_stmt()) {
      ilt_generate_code_receiving(ilt);
      return;
    }
    if (!has_receiving_stmt()) {
      char*& str=ilt->get_out_branches();
      str=generate_code(str, ilt->get_out_def_glob_vars(), ilt->get_out_src_glob_vars());
      return;
    }
    switch (statementtype) {
    case S_BLOCK:
      block->ilt_generate_code(ilt);
      break;
    case S_IF:
      ilt_generate_code_if(ilt);
      break;
    case S_SELECT:
      ilt_generate_code_select(ilt);
      break;
    case S_SELECTUNION:
      ilt_generate_code_select_union(ilt);
      break;
    case S_SELECT_CLASS:
      ilt_generate_code_select_class(ilt);
      break;
    case S_CALL:
      ilt_generate_code_call(ilt);
      break;
    case S_FOR:
      ilt_generate_code_for(ilt);
      break;
    case S_WHILE:
      ilt_generate_code_while(ilt);
      break;
    case S_DOWHILE:
      ilt_generate_code_dowhile(ilt);
      break;
    default:
      FATAL_ERROR("Statement::ilt_generate_code()");
    } // switch statementtype
  }

  char *Statement::generate_code_standalone(char *str)
  {
    const string& tmplabel = my_sb->get_scope_mod_gen()->get_temporary_id();
    const char *label_str = tmplabel.c_str();
    str = mputprintf(str, "{\n"
      "%s:\n", label_str);
    expression_struct expr;
    Code::init_expr(&expr);
    generate_code_expr(&expr);
    str = mputstr(str, expr.preamble);
    str = mputprintf(str, "alt_status alt_flag = ALT_UNCHECKED, "
	"default_flag = ALT_UNCHECKED;\n"
      "TTCN_Snapshot::take_new(FALSE);\n"
      "for ( ; ; ) {\n"
      "if (alt_flag != ALT_NO) {\n"
      "alt_flag = %s;\n"
      "if (alt_flag == ALT_YES) break;\n", expr.expr);
    if (can_repeat()) {
      str = mputprintf(str, "else if (alt_flag == ALT_REPEAT) goto %s;\n",
	label_str);
    }
    str = mputprintf(str, "}\n"
       "if (default_flag != ALT_NO) {\n"
       "default_flag = TTCN_Default::try_altsteps();\n"
       "if (default_flag == ALT_YES || default_flag == ALT_BREAK) break;\n"
       "else if (default_flag == ALT_REPEAT) goto %s;\n"
       "}\n", label_str);
    str = update_location_object(str);
    str = mputprintf(str, "if (alt_flag == ALT_NO && default_flag == ALT_NO) "
      "TTCN_error(\"Stand-alone %s statement failed in file ", get_stmt_name());
    str = Code::translate_string(str, get_filename());
    size_t first_line = get_first_line(), last_line = get_last_line();
    if (first_line < last_line) str = mputprintf(str,
      " between lines %lu and %lu", first_line, last_line);
    else str = mputprintf(str, ", line %lu", first_line);
    str = mputstr(str, ".\");\n"
      "TTCN_Snapshot::take_new(TRUE);\n"
      "}\n");
    str = mputstr(str, expr.postamble);
    str = mputstr(str, "}\n");
    Code::free_expr(&expr);
    return str;
  }

  char *Statement::generate_code_funcinst(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    ref_pard->generate_code(&expr);
    str=Code::merge_free_expr(str, &expr);
    return str;
  }

  char* Statement::generate_code_invoke(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    Value *last_v = fau_refd.value->get_value_refd_last();
    switch(last_v->get_valuetype()) {
    case Value::V_FUNCTION:
    case Value::V_ALTSTEP: {
      Common::Assignment *t_fat = last_v->get_refd_fat();
      expr.expr = mputprintf(expr.expr, "%s(",
	t_fat->get_genname_from_scope(my_sb).c_str());
      fau_refd.ap_list2->generate_code_alias(&expr, t_fat->get_FormalParList(),
	t_fat->get_RunsOnType(), false);
      break; }
    default: {
      fau_refd.value->generate_code_expr_mandatory(&expr);
      Type *t_governor = fau_refd.value->get_expr_governor_last();
      expr.expr = mputprintf(expr.expr, ".%s(",
	t_governor->get_typetype() == Type::T_ALTSTEP ?
	"invoke_standalone" : "invoke");
      fau_refd.ap_list2->generate_code_alias(&expr, t_governor->get_fat_parameters(),
	t_governor->get_fat_runs_on_type(),
        t_governor->get_fat_runs_on_self()); }
    }
    expr.expr = mputc(expr.expr, ')');
    str=Code::merge_free_expr(str, &expr);
    return str;
  }

  char *Statement::generate_code_block(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    switch (block->get_exception_handling()) {
    case StatementBlock::EH_NONE:
      break;
    case StatementBlock::EH_TRY:
      str = mputstr(str, "try ");
      break;
    case StatementBlock::EH_CATCH:
      str = mputstr(str, "catch (const TTCN_Error& ttcn_error) ");
      break;
    default:
      FATAL_ERROR("Statement::generate_code_block()");
    }
    if (!block->is_empty()) {
      str = mputstr(str, "{\n");
      if (debugger_active) {
        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
      }
      str = block->generate_code(str, def_glob_vars, src_glob_vars);
      str = mputstr(str, "}\n");
    } else str = mputstr(str, "/* empty block */;\n");
    return str;
  }

  char *Statement::generate_code_log(char *str)
  {
    if (logargs) {
      bool buffered_mode = true;
      if (logargs->get_nof_logargs() == 1) {
	LogArgument *first_logarg = logargs->get_logarg_byIndex(0);
        switch (first_logarg->get_type()) {
	case LogArgument::L_STR:
	  // the argument is a simple string: use non-buffered mode
	  str = mputstr(str, "TTCN_Logger::log_str(TTCN_USER, \"");
	  str = Code::translate_string(str, first_logarg->get_str().c_str());
	  str = mputstr(str, "\");\n");
	  buffered_mode = false;
	  break;
	case LogArgument::L_MACRO: {
	  Value *t_val = first_logarg->get_val();
	  if (t_val->has_single_expr()) {
	    // the argument is a simple macro call: use non-buffered mode
	    str = mputprintf(str, "TTCN_Logger::log_str(TTCN_USER, %s);\n",
	      t_val->get_single_expr().c_str());
	    buffered_mode = false;
	  } }
	default:
	  break;
	}
      }
      if (buffered_mode) {
	// the argument is a complicated construct: use buffered mode
	str = mputstr(str, "try {\n"
	  "TTCN_Logger::begin_event(TTCN_USER);\n");
	str = logargs->generate_code(str);
	str = mputstr(str, "TTCN_Logger::end_event();\n"
	  "} catch (...) {\n"
	  "TTCN_Logger::finish_event();\n"
	  "throw;\n"
	  "}\n");
      }
    } else {
      // the argument is missing
      str = mputstr(str, "TTCN_Logger::log_str(TTCN_USER, "
	"\"<empty log statement>\");\n");
    }
    return str;
  }

  char *Statement::generate_code_testcase_stop(char *str)
  {
    if (logargs) str = generate_code_log(str);
    str = mputstr(str, "TTCN_error(\"testcase.stop\");\n");
    return str;
  }

  char *Statement::generate_code_label(char *str)
  {
    if (label.used) {
      return mputprintf(str, "%s: /* TTCN-3 label: %s */;\n",
	get_clabel().c_str(), label.id->get_dispname().c_str());
    } else {
      return mputprintf(str, "/* unused TTCN-3 label: %s */;\n",
	label.id->get_dispname().c_str());
    }
  }

  char *Statement::generate_code_goto(char *str)
  {
    if (!go_to.label) FATAL_ERROR("Statement::generate_code_goto()");
    return mputprintf(str, "goto %s; /* TTCN-3 label: %s */\n",
      go_to.label->get_clabel().c_str(), go_to.id->get_dispname().c_str());
    return str;
  }

  char* Statement::generate_code_if(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    size_t blockcount=0;
    bool unreach=false, eachfalse=true;
    str=if_stmt.ics->generate_code(str, def_glob_vars, src_glob_vars,
      blockcount, unreach, eachfalse);
    if(if_stmt.elseblock && !unreach) {
      if(!eachfalse) str=mputstr(str, "else ");
      eachfalse=false;
      str=mputstr(str, "{\n");
      if (debugger_active) {
        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
      }
      blockcount++;
      str=if_stmt.elseblock->generate_code(str, def_glob_vars, src_glob_vars);
    }
    while(blockcount-->0) str=mputstr(str, "}\n");
    if(eachfalse) str=mputstr(str, "/* never occurs */;\n");
    return str;
  }

  char* Statement::generate_code_select(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    const string& tmp_prefix = my_sb->get_scope_mod_gen()->get_temporary_id();
    char *expr_init=memptystr();
    char *expr_name=select.expr->generate_code_tmp(0, expr_init);
    if (expr_init[0]) { // some init code was generated
      str = update_location_object(str);
    }
    str = mputstr(str, "{\n");
    if (expr_init[0]) {
      str = mputstr(str, expr_init);
    }
    
    // Calculate the head expression only once
    const string& tmp_id = my_sb->get_scope_mod_gen()->get_temporary_id();
    str = mputprintf(str, "const %s &%s = %s;\n",
      select.expr->get_my_governor()->get_genname_value(select.expr->get_my_scope()).c_str(),
      tmp_id.c_str(),
      expr_name);
    // We generate different code if the head is an integer and all the branches
    // are foldable and not big integers
    bool gen_switch_code = false;
    if (select.expr->get_my_governor()->is_compatible_tt(Type::T_INT, select.expr->is_asn1())) {
      gen_switch_code = select.scs->can_generate_switch();
    }
    if (gen_switch_code) {
      str=select.scs->generate_code_switch(str, def_glob_vars, src_glob_vars, tmp_id.c_str());
    }
    else {
      str=select.scs->generate_code(str, def_glob_vars, src_glob_vars, tmp_prefix.c_str(), tmp_id.c_str());
    }
    Free(expr_name);
    str=mputstr(str, "}\n");
    Free(expr_init);
    return str;
  }
  
  char* Statement::generate_code_select_union(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    select_union.expr->generate_code_expr_mandatory(&expr);
    if (expr.preamble) {
      str = mputstr(str, expr.preamble);
    }
    str = mputprintf(str, "switch(%s.get_selection()) {\n", expr.expr);
    string type_name_str = select_union.expr->get_expr_governor_last()->get_genname_value(select_union.expr->get_my_scope());
    const char* type_name = type_name_str.c_str();
    char* loc = NULL;
    loc = select_union.expr->update_location_object(loc);
    str = select_union.sus->generate_code(str, def_glob_vars, src_glob_vars, type_name, loc);
    str = mputstr(str, "}\n");
    if (expr.postamble) {
      str = mputstr(str, expr.postamble);
    }
    Code::free_expr(&expr);
    Free(loc);
    return str;
  }
  
  char* Statement::generate_code_select_class(char* str, char*& def_glob_vars, char*& src_glob_vars)
  {
    const string& tmp_prefix = my_sb->get_scope_mod_gen()->get_temporary_id();
    str = mputstr(str, "{\n");
    expression_struct_t expr;
    Code::init_expr(&expr);
    select_class.ref->generate_code(&expr);
    if (expr.preamble != NULL) {
      str = mputstr(str, expr.preamble);
    }
    str = select_class.sccs->generate_code(str, def_glob_vars, src_glob_vars,
      tmp_prefix.c_str(), expr.expr);
    if (expr.postamble != NULL) {
      str = mputstr(str, expr.postamble);
    }
    Code::free_expr(&expr);
    str = mputstr(str, "}\n");
    return str;
  }
  
  char *Statement::generate_code_for(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    /** \todo initial does not have its own location */
    // statements in initial may have side effects
    // generate code for them anyway
    if (loop.for_stmt.varinst) {
      str = mputstr(str, "{\n");
      if (debugger_active) {
        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
      }
      str = loop.for_stmt.init_varinst->generate_code_str(str);
    } else {
      str = loop.for_stmt.init_ass->update_location_object(str);
      str = loop.for_stmt.init_ass->generate_code(str);
    }
    // check whether the final expression is constant
    bool final_is_true = false, final_is_false = false;
    if (!loop.for_stmt.finalexpr->is_unfoldable()) {
      if (loop.for_stmt.finalexpr->get_val_bool()) final_is_true = true;
      else final_is_false = true;
    }
    if (final_is_false) str = mputstr(str, "/* never occurs */;\n");
    else {
      if (loop.has_cnt)
        loop.label_next =
          new string(my_sb->get_scope_mod_gen()->get_temporary_id());
      str = update_location_object(str);
      str = mputstr(str, "for ( ; ; ) {\n");
      // do not generate the exit condition for infinite loops
      if (!final_is_true) {
	str = loop.for_stmt.finalexpr->update_location_object(str);
	size_t blockcount = 0;
	str = loop.for_stmt.finalexpr->generate_code_tmp(str, "if (!",
	  blockcount);
	str = mputstr(str, ") break;\n");
	while (blockcount-- > 0) str = mputstr(str, "}\n");
      }
      if (loop.label_next) str = mputstr(str, "{\n");
      if (debugger_active) {
        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
      }
      str = loop.block->generate_code(str, def_glob_vars, src_glob_vars);
      if (loop.label_next)
        str = mputprintf(str, "}\n"
          "%s:\n", loop.label_next->c_str());
      str = loop.for_stmt.step->update_location_object(str);
      str = loop.for_stmt.step->generate_code(str);
      str = mputstr(str, "}\n");
    }
    if (loop.for_stmt.varinst) str = mputstr(str, "}\n");
    return str;
  }

  char *Statement::generate_code_while(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    // check whether the expression is constant
    bool condition_always_true = false, condition_always_false = false;
    if (!loop.expr->is_unfoldable()) {
      if (loop.expr->get_val_bool()) condition_always_true = true;
      else condition_always_false = true;
    }
    if (condition_always_false) str = mputstr(str, "/* never occurs */;\n");
    else {
      str = mputstr(str, "for ( ; ; ) {\n");
      if (loop.has_cnt_in_ags) {
        loop.label_next =
          new string(my_sb->get_scope_mod_gen()->get_temporary_id());
        str = mputprintf(str, "%s:\n", loop.label_next->c_str());
      }
      // do not generate the exit condition for infinite loops
      if (!condition_always_true) {
	str = loop.expr->update_location_object(str);
	size_t blockcount = 0;
	str = loop.expr->generate_code_tmp(str, "if (!", blockcount);
	str = mputstr(str, ") break;\n");
	while(blockcount-- > 0) str = mputstr(str, "}\n");
      }
      if (debugger_active) {
        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
      }
      str = loop.block->generate_code(str, def_glob_vars, src_glob_vars);
      str = mputstr(str, "}\n");
    }
    return str;
  }

  char *Statement::generate_code_dowhile(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    // check whether the expression is constant
    bool expr_is_const = !loop.expr->is_unfoldable();
    bool is_infinite_loop = false;
    if (expr_is_const) {
      if (loop.expr->get_val_bool()) is_infinite_loop = true;
      else loop.iterate_once = true;
    }
    if (loop.iterate_once && !loop.has_brk && !loop.has_cnt) {
      str = mputstr(str, "{\n");
      if (debugger_active) {
        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
      }
      str = loop.block->generate_code(str, def_glob_vars, src_glob_vars);
    } else {
      str = mputstr(str, "for ( ; ; ) {\n");
      if (loop.has_cnt_in_ags || (!expr_is_const && loop.has_cnt))
        loop.label_next =
          new string(my_sb->get_scope_mod_gen()->get_temporary_id());
      if (loop.label_next && is_infinite_loop)
        str = mputprintf(str, "%s:\n", loop.label_next->c_str());
      if (loop.label_next && !is_infinite_loop) str = mputstr(str, "{\n");
      if (debugger_active) {
        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
      }
      str = loop.block->generate_code(str, def_glob_vars, src_glob_vars);
      // do not generate the exit condition for infinite loops
      if (!is_infinite_loop) {
        if (loop.label_next)
          str = mputprintf(str, "}\n"
            "%s:\n", loop.label_next->c_str());
        str = loop.expr->update_location_object(str);
        if (loop.iterate_once) str = mputstr(str, "break;\n");
        else {
          size_t blockcount = 0;
          str = loop.expr->generate_code_tmp(str, "if (!", blockcount);
          str = mputstr(str, ") break;\n");
          while (blockcount-- > 0) str = mputstr(str, "}\n");
        }
      }
    }
    str = mputstr(str, "}\n");
    return str;
  }

  char *Statement::generate_code_break(char *str)
  {
    // in altstep (2 (=2nd if branch))
    // in alt and loops - not inside interleave (4)
    // in loops without receiving statement embedded in interleave (4)
    // in loops with receiving statement embedded in interleave (1)
    // in interleave when not embedded in enclosed loop or alt/interleave (4)
    // in alt/interleave embedded in interleave (3)
    if (brk_cnt.loop_stmt && brk_cnt.loop_stmt->loop.il_label_end)
      str=mputprintf(str, "goto %s;\n",
            brk_cnt.loop_stmt->loop.il_label_end->c_str());
    else if (brk_cnt.ags && brk_cnt.ags->get_is_altstep())
      str=mputstr(str, "return ALT_BREAK;\n");
    else if (brk_cnt.ags && brk_cnt.ags->get_il_label_end())
      str=mputprintf(str, "goto %s;\n",
            brk_cnt.ags->get_il_label_end()->c_str());
    else
      str=mputstr(str, "break;\n");
    return str;
  }

  char *Statement::generate_code_continue(char *str)
  {
    // not inside interleave when continue is not inside embedded ags (2 or 1)
    // continue is inside ags enclosed in the loop (3)
    // in interleave (3, 2 or 1)
    if (brk_cnt.loop_stmt != 0) {
      if (brk_cnt.loop_stmt->loop.iterate_once && !brk_cnt.ags &&
          !brk_cnt.loop_stmt->loop.is_ilt)
        str=mputstr(str, "break;\n");
      else {
        if (!brk_cnt.loop_stmt->loop.label_next)
          str=mputstr(str, "continue;\n");
        else
          str=mputprintf(str, "goto %s;\n",
            brk_cnt.loop_stmt->loop.label_next->c_str());
      }
    } else
      FATAL_ERROR("Statement::generate_code_continue()");
    return str;
  }

  char *Statement::generate_code_repeat(char *str)
  {
    string *tmplabel=ags->get_label();
    if(!tmplabel) str=mputstr(str, "return ALT_REPEAT;\n");
    else str=mputprintf(str, "goto %s;\n", tmplabel->c_str());
    return str;
  }

  char* Statement::generate_code_interleave(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    ILT_root ilt(this, def_glob_vars, src_glob_vars);
    str=ilt.generate_code(str);
    return str;
  }

  void Statement::ilt_generate_code_interleave(ILT *ilt)
  {
    const string& mytmpid=ilt->get_my_tmpid();
    bool toplevel=ilt->is_toplevel();
    size_t goto_label_num=toplevel?static_cast<size_t>(-1):ilt->get_new_label_num();
    char*& out_branches=ilt->get_out_branches();
    out_branches=update_location_object(out_branches);
    string state_cond;
    if(toplevel) state_cond="";
    else {
      char *label_end = mprintf("%s_l%lu", mytmpid.c_str(),
        static_cast<unsigned long>( goto_label_num ));
      ags->set_il_label_end (label_end);
      Free(label_end);
      ILT_branch *branch=ilt->get_as_branch();
      size_t state_var=branch->get_my_state_var();
      size_t state_var_val=ilt->get_new_state_var_val(state_var);
      state_cond=branch->get_state_cond();
      if(!state_cond.empty()) state_cond+=" && ";
      char *s=mprintf("%s_state[%lu]==%lu", mytmpid.c_str(),
                      static_cast<unsigned long>( state_var ), static_cast<unsigned long>( state_var_val ));
      state_cond+=s;
      Free(s);
      out_branches=mputprintf(out_branches, "%s_state[%lu]=%lu;\n",
        mytmpid.c_str(),
        static_cast<unsigned long>( state_var), static_cast<unsigned long>( state_var_val ));
    }
    for(size_t i=0; i<ags->get_nof_ags(); i++) {
      AltGuard *ag=ags->get_ag_byIndex(i);
      if(ag->get_type()!=AltGuard::AG_OP)
        FATAL_ERROR("Statement::ilt_generate_code_interleave()");
      size_t state_var=ilt->get_new_state_var(toplevel);
      size_t state_var_val=ilt->get_new_state_var_val(state_var);
      ilt->add_branch(new ILT_branch(ILT_branch::BT_IL, ag, state_cond,
                                     state_var, state_var_val,
                                     goto_label_num));
      if(!toplevel)
        out_branches=mputprintf(out_branches, "%s_state[%lu]=%lu;\n",
          mytmpid.c_str(),
          static_cast<unsigned long>( state_var), static_cast<unsigned long>( state_var_val ));
    } // for
    if(!toplevel)
      out_branches=mputprintf(out_branches, "goto %s;\n"
                              "%s_l%lu:\n",
                              mytmpid.c_str(),
                              mytmpid.c_str(), static_cast<unsigned long>( goto_label_num ));
  }

  void Statement::ilt_generate_code_alt(ILT *ilt)
  {
    const string& mytmpid=ilt->get_my_tmpid();
    size_t goto_label_num=ilt->get_new_label_num();
    char *label_end = mprintf("%s_l%lu", mytmpid.c_str(),
      static_cast<unsigned long>( goto_label_num ));
    ags->set_il_label_end (label_end);
    ILT_branch *branch=ilt->get_as_branch();
    string state_cond=branch->get_state_cond();
    size_t state_var=branch->get_my_state_var();
    size_t state_var_val=ilt->get_new_state_var_val(state_var);
    char*& out_branches=ilt->get_out_branches();
    for(size_t i=0; i<ags->get_nof_ags(); i++) {
      AltGuard *ag=ags->get_ag_byIndex(i);
      if(ag->get_type()!=AltGuard::AG_OP)
        FATAL_ERROR("Statement::ilt_generate_code_alt()");
      ilt->add_branch(new ILT_branch(ILT_branch::BT_ALT, ag, state_cond,
                                     state_var, state_var_val,
                                     goto_label_num));
    } // for
    out_branches=update_location_object(out_branches);
    out_branches=mputprintf(out_branches, "%s_state[%lu]=%lu;\n"
                            "goto %s;\n"
                            "%s:\n",
                            mytmpid.c_str(), static_cast<unsigned long>( state_var ),
                            static_cast<unsigned long>( state_var_val ),
                            mytmpid.c_str(), label_end);
    Free(label_end);
  }

  void Statement::ilt_generate_code_receiving(ILT *ilt)
  {
    const string& mytmpid=ilt->get_my_tmpid();
    size_t goto_label_num=ilt->get_new_label_num();
    ILT_branch *branch=ilt->get_as_branch();
    string state_cond=branch->get_state_cond();
    size_t state_var=branch->get_my_state_var();
    size_t state_var_val=ilt->get_new_state_var_val(state_var);
    char*& out_branches=ilt->get_out_branches();
    ilt->add_branch(new ILT_branch(ILT_branch::BT_RECV, this, state_cond,
                                   state_var, state_var_val, goto_label_num));
    out_branches=update_location_object(out_branches);
    out_branches=mputprintf(out_branches, "%s_state[%lu]=%lu;\n"
                            "goto %s;\n"
                            "%s_l%lu:\n",
                            mytmpid.c_str(), static_cast<unsigned long>( state_var ),
                            static_cast<unsigned long>( state_var_val ),
                            mytmpid.c_str(), mytmpid.c_str(),
                            static_cast<unsigned long>( goto_label_num ));
  }

  void Statement::ilt_generate_code_def(ILT *ilt)
  {
    char*& str=ilt->get_out_branches();
    str=update_location_object(str);
    {
      char *genname=mprintf("%s_d%lu_%s", ilt->get_my_tmpid().c_str(),
                            static_cast<unsigned long>( ilt->get_new_tmpnum() ),
			    def->get_id().get_name().c_str());
      def->set_genname(string(genname));
      Free(genname);
    }
    def->ilt_generate_code(ilt);
  }

  void Statement::ilt_generate_code_if(ILT *ilt)
  {
    char *end_label=mprintf("%s_l%lu",
                             ilt->get_my_tmpid().c_str(),
                             static_cast<unsigned long>( ilt->get_new_label_num() ));
    bool unreach=false;
    if_stmt.ics->ilt_generate_code(ilt, end_label, unreach);
    if(if_stmt.elseblock && !unreach)
      if_stmt.elseblock->ilt_generate_code(ilt);
    char*& str=ilt->get_out_branches();
    str=mputprintf(str, "%s:\n", end_label);
    Free(end_label);
  }

  void Statement::ilt_generate_code_select(ILT *ilt)
  {
    char*& str=ilt->get_out_branches();
    str=update_location_object(str);
    const string& tmp_prefix = my_sb->get_scope_mod_gen()->get_temporary_id();
    char *expr_init=memptystr();
    char *expr_name=select.expr->generate_code_tmp(0, expr_init);
    
    // Calculate the head expression only once
    const string& tmp_id = my_sb->get_scope_mod_gen()->get_temporary_id();
    char * head_expr = mprintf("const %s &%s = %s;\n",
      select.expr->get_my_governor()->get_genname_value(select.expr->get_my_scope()).c_str(),
      tmp_id.c_str(),
      expr_name);

    // We generate different code if the head is an integer and all the branches
    // are foldable
    bool gen_switch_code = false;
    if (select.expr->get_my_governor()->is_compatible_tt(Type::T_INT, select.expr->is_asn1())) {
      gen_switch_code = select.scs->can_generate_switch();
    }
    if (gen_switch_code) {
      select.scs->ilt_generate_code_switch(ilt, expr_init, head_expr, tmp_id.c_str());
    }
    else {
      select.scs->ilt_generate_code(ilt, tmp_prefix.c_str(),
                                  expr_init, head_expr, tmp_id.c_str());
    }
    Free(head_expr);
    Free(expr_name);
    Free(expr_init);
  }
  
  void Statement::ilt_generate_code_select_union(ILT *ilt)
  {
    char*& str = ilt->get_out_branches();
    expression_struct expr;
    Code::init_expr(&expr);
    select_union.expr->generate_code_expr(&expr);
    bool has_recv = select_union.sus->has_receiving_stmt();
    if (has_recv) {
      str = mputstr(str, "{\n");
    }
    if (expr.preamble) {
      str = mputstr(str, expr.preamble);
    }
    str = mputprintf(str, "switch(%s.get_selection()) {\n", expr.expr);
    string type_name_str = select_union.expr->get_expr_governor_last()->get_genname_value(select_union.expr->get_my_scope());
    const char* type_name = type_name_str.c_str();
    char* loc = NULL;
    loc = select_union.expr->update_location_object(loc);
    str = select_union.sus->generate_code(str, ilt->get_out_def_glob_vars(),
      ilt->get_out_src_glob_vars(), type_name, loc);
    str = mputstr(str, "}\n");
    if (expr.postamble) {
      str = mputstr(str, expr.postamble);
    }
    Code::free_expr(&expr);
    Free(loc);
    if (has_recv) {
      str = mputstr(str, "}\n");
    }
  }
  
  void Statement::ilt_generate_code_select_class(ILT* ilt)
  {
    const string& tmp_prefix = my_sb->get_scope_mod_gen()->get_temporary_id();
    char*& str = ilt->get_out_branches();
    expression_struct expr;
    Code::init_expr(&expr);
    select_class.ref->generate_code(&expr);
    bool has_recv = select_class.sccs->has_receiving_stmt();
    if (has_recv) {
      str = mputstr(str, "{\n");
    }
    if (expr.preamble != NULL) {
      str = mputstr(str, expr.preamble);
    }
    select_class.sccs->ilt_generate_code(ilt, tmp_prefix.c_str(), expr.expr);
    if (expr.postamble != NULL) {
      str = mputstr(str, expr.postamble);
    }
    Code::free_expr(&expr);
    if (has_recv) {
      str = mputstr(str, "}\n");
    }
  }

  void Statement::ilt_generate_code_call(ILT *ilt)
  {
    char*& str=ilt->get_out_branches();
    str=update_location_object(str);
    expression_struct expr;
    Code::init_expr(&expr);
    port_op.portref->generate_code(&expr);
    expr.expr = mputstr(expr.expr, ".call(");
    port_op.s.sendpar->generate_code(&expr);
    if(port_op.s.toclause) {
      expr.expr = mputstr(expr.expr, ", ");
      port_op.s.toclause->generate_code_expr(&expr);
    }
    expr.expr = mputc(expr.expr, ')');
    str = Code::merge_free_expr(str, &expr);
    if (port_op.s.call.body) {
      str = mputstr(str, "{\n"); // (1)
      if (port_op.s.call.timer) {
	str = port_op.s.call.timer->update_location_object(str);
	str = mputstr(str, "TIMER call_timer;\n");
	Code::init_expr(&expr);
	expr.expr = mputstr(expr.expr, "call_timer.start(");
	port_op.s.call.timer->generate_code_expr(&expr);
	expr.expr = mputc(expr.expr, ')');
	str = Code::merge_free_expr(str, &expr);
      }
      // the label name is used for prefixing local variables
      if(!my_sb) FATAL_ERROR("Statement::generate_code_call()");
      const string& tmplabel = my_sb->get_scope_mod_gen()->get_temporary_id();
      str = port_op.s.call.body->generate_code_call_body(str,
        ilt->get_out_def_glob_vars(), ilt->get_out_src_glob_vars(), *this,
        tmplabel, true);
      const char *label_str = tmplabel.c_str();
      str=mputprintf(str, "goto %s_end;\n"
                     "}\n", // (1)
                     label_str);
      port_op.s.call.body->ilt_generate_code_call_body(ilt, label_str);
      str=mputprintf(str, "%s_end:\n", label_str);
    }
  }

  void Statement::ilt_generate_code_for(ILT *ilt)
  {
    char*& str = ilt->get_out_branches();
    str = update_location_object(str);
    // statements in initial may have side effects
    // generate code for them anyway
    if (loop.for_stmt.varinst) {
      char *genname = mprintf("%s_d%lu_", ilt->get_my_tmpid().c_str(),
	static_cast<unsigned long>( ilt->get_new_tmpnum() ));
      loop.for_stmt.init_varinst->set_genname(string(genname));
      Free(genname);
      loop.for_stmt.init_varinst->ilt_generate_code(ilt);
    } else str = loop.for_stmt.init_ass->generate_code(str);
    // check whether the final expression is constant
    bool final_is_true = false, final_is_false = false;
    if (!loop.for_stmt.finalexpr->is_unfoldable()) {
      if (loop.for_stmt.finalexpr->get_val_bool()) final_is_true = true;
      else final_is_false = true;
    }
    if (final_is_false) str = mputstr(str, "/* never occurs */;\n");
    else {
      char *label_prefix = mprintf("%s_l%lu_", ilt->get_my_tmpid().c_str(),
	static_cast<unsigned long>( ilt->get_new_label_num() ));
      str = mputprintf(str, "%sbegin:\n", label_prefix);
      // do not generate the exit condition for infinite loops
      if (!final_is_true) {
	str = loop.for_stmt.finalexpr->update_location_object(str);
	size_t blockcount = 0;
	str = loop.for_stmt.finalexpr->generate_code_tmp(str, "if (!",
	  blockcount);
	str = mputprintf(str, ") goto %send;\n", label_prefix);
	while (blockcount-- > 0) str = mputstr(str, "}\n");
      }
      loop.is_ilt = true;
      if (loop.has_brk) {
        loop.il_label_end = new string(label_prefix);
        *loop.il_label_end += "end";
      }
      if (loop.has_cnt) {
        loop.label_next = new string(label_prefix);
        *loop.label_next += "next";
      }
      loop.block->ilt_generate_code(ilt);
      if (loop.label_next)
        str = mputprintf(str, "%snext:\n", label_prefix);
      str = update_location_object(str);
      str = loop.for_stmt.step->generate_code(str);
      str = mputprintf(str, "goto %sbegin;\n", label_prefix);
      if (!final_is_true || loop.has_brk)
        str = mputprintf(str, "%send:\n", label_prefix);
      Free(label_prefix);
    }
  }

  void Statement::ilt_generate_code_while(ILT *ilt)
  {
    char*& str = ilt->get_out_branches();
    // Location need not be set here; the location is set for the expression.
    // check whether the expression is constant
    bool expr_is_true = false, expr_is_false = false;
    if (!loop.expr->is_unfoldable()) {
      if (loop.expr->get_val_bool()) expr_is_true = true;
      else expr_is_false = true;
    }
    if (expr_is_false) str = mputstr(str, "/* never occurs */;\n");
    else {
      char *label_prefix = mprintf("%s_l%lu_", ilt->get_my_tmpid().c_str(),
	static_cast<unsigned long>( ilt->get_new_label_num() ));
      str = mputprintf(str, "%sbegin:\n", label_prefix);
      loop.is_ilt = true;
      if (loop.has_brk) {
        loop.il_label_end = new string(label_prefix);
        *loop.il_label_end += "end";
      }
      if (loop.has_cnt) {
        loop.label_next = new string(label_prefix);
        *loop.label_next += "begin";
      }
      // do not generate the exit condition for infinite loops
      if (!expr_is_true) {
	str = loop.expr->update_location_object(str);
	size_t blockcount = 0;
	str = loop.expr->generate_code_tmp(str, "if (!", blockcount);
	str = mputprintf(str, ") goto %send;\n", label_prefix);
	while (blockcount-- > 0) str = mputstr(str, "}\n");
      }
      loop.block->ilt_generate_code(ilt);
      str = update_location_object(str);
      str = mputprintf(str, "goto %sbegin;\n", label_prefix);
      if (!expr_is_true || loop.has_brk)
        str = mputprintf(str, "%send:\n", label_prefix);
      Free(label_prefix);
    }
  }

  void Statement::ilt_generate_code_dowhile(ILT *ilt)
  {
    char*& str = ilt->get_out_branches();
    // Location need not be set here; there is only a label before the body.
    // check whether the expression is constant
    bool expr_is_true = false;
    if (!loop.expr->is_unfoldable()) {
      if (loop.expr->get_val_bool()) expr_is_true = true;
      else loop.iterate_once = true;
    }
    char *label_prefix = 0;
    if (!loop.iterate_once || loop.has_brk || loop.has_cnt)
      label_prefix = mprintf("%s_l%lu_", ilt->get_my_tmpid().c_str(),
	static_cast<unsigned long>( ilt->get_new_label_num() ));
    loop.is_ilt = true;
    if (loop.has_brk) {
      loop.il_label_end = new string(label_prefix);
      *loop.il_label_end += "end";
    }
    if (loop.has_cnt)
      loop.label_next = new string(label_prefix);
    if (loop.iterate_once) {
      if (loop.label_next) *loop.label_next += "end";
      loop.block->ilt_generate_code(ilt);
    } else {
      str = mputprintf(str, "%sbegin:\n", label_prefix);
      if (loop.label_next)
        *loop.label_next += (expr_is_true ? "begin" : "next");
      loop.block->ilt_generate_code(ilt);
      if (expr_is_true) str = mputprintf(str, "goto %sbegin;\n", label_prefix);
      else {
        if (loop.label_next) str = mputprintf(str, "%snext:\n", label_prefix);
	str = loop.expr->update_location_object(str);
	size_t blockcount = 0;
	str = loop.expr->generate_code_tmp(str, "if (", blockcount);
	str = mputprintf(str, ") goto %sbegin;\n", label_prefix);
	while (blockcount-- > 0) str = mputstr(str, "}\n");
      }
    }
    if (loop.il_label_end || (loop.iterate_once && loop.label_next)) {
      str = mputprintf(str, "%send: ;\n", label_prefix);
    }
    Free(label_prefix);
  }

  char *Statement::generate_code_return(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    expr.expr = mputstr(expr.expr, "return");
    Definition *my_def = my_sb->get_my_def();
    if (returnexpr.v) {
      expr.expr = mputc(expr.expr, ' ');
      if (debugger_active) {
        // the debugger's return value storing macro requires a temporary,
        // so the returned expression isn't evaluated twice
        string tmp_id = my_def->get_my_scope()->get_scope_mod_gen()->get_temporary_id();
        expr.preamble = mputprintf(expr.preamble, "%s %s;\n",
          returnexpr.v->get_expr_governor_last()->get_genname_value(my_def->get_my_scope()).c_str(),
          tmp_id.c_str());
        expr.expr = mputprintf(expr.expr, "DEBUGGER_STORE_RETURN_VALUE(%s, ", tmp_id.c_str());
      }
      returnexpr.v->generate_code_expr_mandatory(&expr);
      if (debugger_active) {
        expr.expr = mputc(expr.expr, ')');
      }
    } else if (returnexpr.t) {
      expr.expr = mputc(expr.expr, ' ');
      if (!my_def) FATAL_ERROR("Statement::generate_code_return()");
      if (debugger_active) {
        // the debugger's return value storing macro requires a temporary,
        // so the returned expression isn't evaluated twice
        string tmp_id = my_def->get_my_scope()->get_scope_mod_gen()->get_temporary_id();
        expr.preamble = mputprintf(expr.preamble, "%s_template %s;\n",
          returnexpr.t->get_my_governor()->get_genname_value(my_def->get_my_scope()).c_str(),
          tmp_id.c_str());
        expr.expr = mputprintf(expr.expr, "DEBUGGER_STORE_RETURN_VALUE(%s, ", tmp_id.c_str());
      }
      Def_Function_Base* dfb = dynamic_cast<Def_Function_Base*>(my_def);
      if (!dfb) FATAL_ERROR("Statement::generate_code_return()");
      if (dfb->get_template_restriction() != TR_NONE &&
          returnexpr.gen_restriction_check) {
        returnexpr.t->generate_code_expr(&expr,
          dfb->get_template_restriction());
      } else {
        returnexpr.t->generate_code_expr(&expr, TR_NONE);
      }
      if (debugger_active) {
        expr.expr = mputc(expr.expr, ')');
      }
    } else {
      if (my_def && my_def->get_asstype() == Definition::A_ALTSTEP)
        expr.expr = mputstr(expr.expr, " ALT_YES");
      // else it's a return with no value: the only case a blank is unneeded
    }
    return Code::merge_free_expr(str, &expr);
  }

  char *Statement::generate_code_activate(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    expr.expr = mputprintf(expr.expr, "%s(", ref_pard->get_refd_assignment()
	->get_genname_from_scope(my_sb, "activate_").c_str());
    ref_pard->get_parlist()->generate_code_noalias(&expr, ref_pard->get_refd_assignment()->get_FormalParList());
    expr.expr = mputc(expr.expr, ')');
    return Code::merge_free_expr(str, &expr);
  }

  char *Statement::generate_code_activate_refd(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    Value *last_v = fau_refd.value->get_value_refd_last();
    if (last_v->get_valuetype() == Value::V_ALTSTEP) {
      expr.expr = mputprintf(expr.expr, "%s(", last_v->get_refd_fat()
	  ->get_genname_from_scope(my_sb, "activate_").c_str());
    } else {
      fau_refd.value->generate_code_expr_mandatory(&expr);
      expr.expr = mputstr(expr.expr, ".activate(");
    }
    fau_refd.ap_list2->generate_code_noalias(&expr, NULL);
    expr.expr = mputc(expr.expr, ')');
    return Code::merge_free_expr(str, &expr);
  }

  char *Statement::generate_code_deactivate(char *str)
  {
    if(!deactivate) str=mputstr(str, "TTCN_Default::deactivate_all();\n");
    else {
      expression_struct expr;
      Code::init_expr(&expr);
      expr.expr=mputstr(expr.expr, "TTCN_Default::deactivate(");
      deactivate->generate_code_expr(&expr);
      expr.expr=mputstr(expr.expr, ");\n");
      str=Code::merge_free_expr(str, &expr);
    }
    return str;
  }

  char *Statement::generate_code_send(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    if (!port_op.translate) {
      port_op.portref->generate_code(&expr);
      expr.expr = mputstr(expr.expr, ".send(");
    } else {
      expr.expr = mputstr(expr.expr, "send(");
    }
    generate_code_expr_sendpar(&expr);
    if (port_op.s.toclause) {
      expr.expr = mputstr(expr.expr, ", ");
      port_op.s.toclause->generate_code_expr(&expr);
    }
    expr.expr = mputstr(expr.expr, ", ");
    generate_code_expr_timestamp_redirect(&expr, true);
    expr.expr = mputc(expr.expr, ')');
    return Code::merge_free_expr(str, &expr);
  }

  char *Statement::generate_code_call(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    port_op.portref->generate_code(&expr);
    expr.expr = mputstr(expr.expr, ".call(");
    port_op.s.sendpar->generate_code(&expr);
    if(port_op.s.toclause) {
      expr.expr = mputstr(expr.expr, ", ");
      port_op.s.toclause->generate_code_expr(&expr);
    }
    expr.expr = mputstr(expr.expr, ", ");
    generate_code_expr_timestamp_redirect(&expr, true);
    expr.expr = mputc(expr.expr, ')');
    str = Code::merge_free_expr(str, &expr);
    if (port_op.s.call.body) {
      str = mputstr(str, "{\n");
      if (port_op.s.call.timer) {
        str = port_op.s.call.timer->update_location_object(str);
        str = mputstr(str, "TIMER call_timer;\n");
        Code::init_expr(&expr);
        expr.expr = mputstr(expr.expr, "call_timer.start(");
        port_op.s.call.timer->generate_code_expr(&expr);
        expr.expr = mputc(expr.expr, ')');
        str = Code::merge_free_expr(str, &expr);
      }
      // the label name is used for prefixing local variables
      if(!my_sb) FATAL_ERROR("Statement::generate_code_call()");
      str = port_op.s.call.body->generate_code_call_body(str, def_glob_vars, src_glob_vars, *this,
	my_sb->get_scope_mod_gen()->get_temporary_id(), false);
      str=mputstr(str, "}\n");
    }
    return str;
  }

  char *Statement::generate_code_reply(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    port_op.portref->generate_code(&expr);
    expr.expr=mputstr(expr.expr, ".reply(");
    port_op.s.sendpar->generate_code(&expr);
    if(port_op.s.replyval) {
      expr.expr=mputstr(expr.expr, ".set_value_template(");
      port_op.s.replyval->generate_code_expr(&expr);
      expr.expr=mputc(expr.expr, ')');
    }
    if(port_op.s.toclause) {
      expr.expr=mputstr(expr.expr, ", ");
      port_op.s.toclause->generate_code_expr(&expr);
    }
    expr.expr = mputstr(expr.expr, ", ");
    generate_code_expr_timestamp_redirect(&expr, true);
    expr.expr=mputc(expr.expr, ')');
    return Code::merge_free_expr(str, &expr);
  }

  char *Statement::generate_code_raise(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    port_op.portref->generate_code(&expr);
    expr.expr=mputstr(expr.expr, ".raise(");
    port_op.s.raise.signature_ref->generate_code(&expr);
    expr.expr=mputstr(expr.expr, "_exception(");
    generate_code_expr_sendpar(&expr);
    expr.expr=mputc(expr.expr, ')');
    if(port_op.s.toclause) {
      expr.expr=mputstr(expr.expr, ", ");
      port_op.s.toclause->generate_code_expr(&expr);
    }
    expr.expr = mputstr(expr.expr, ", ");
    generate_code_expr_timestamp_redirect(&expr, true);
    expr.expr=mputc(expr.expr, ')');
    return Code::merge_free_expr(str, &expr);
  }

  char *Statement::generate_code_portop(char *str, const char *opname)
  {
    if (port_op.portref) {
      expression_struct expr;
      Code::init_expr(&expr);
      port_op.portref->generate_code(&expr);
      expr.expr=mputprintf(expr.expr, ".%s()", opname);
      str=Code::merge_free_expr(str, &expr);
    } else {
      str = mputprintf(str, "PORT::all_%s();\n", opname);
    }
    return str;
  }

  char *Statement::generate_code_startcomp(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    Common::Assignment *func = comp_op.funcinstref->get_refd_assignment();
    expr.expr = mputprintf(expr.expr, "%s(",
      func->get_genname_from_scope(my_sb, "start_").c_str());
    comp_op.compref->generate_code_expr(&expr);
    FormalParList *fplist = func->get_FormalParList();
    if (fplist->get_nof_fps() > 0) {
      expr.expr = mputstr(expr.expr, ", ");
      comp_op.funcinstref->get_parlist()->generate_code_noalias(&expr, fplist);
    }
    expr.expr = mputc(expr.expr, ')');
    return Code::merge_free_expr(str, &expr);
  }

  char *Statement::generate_code_startcomp_refd(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    Value *last_v = comp_op.derefered.value->get_value_refd_last();
    if (last_v->get_valuetype() == Value::V_FUNCTION) {
      expr.expr = mputprintf(expr.expr, "%s(", last_v->get_refd_fat()
	  ->get_genname_from_scope(my_sb, "start_").c_str());
    } else {
      comp_op.derefered.value->generate_code_expr_mandatory(&expr);
      expr.expr = mputstr(expr.expr, ".start(");
    }
    comp_op.compref->generate_code_expr(&expr);
    if (comp_op.derefered.ap_list2->get_nof_pars() > 0) {
      expr.expr = mputstr(expr.expr, ", ");
      comp_op.derefered.ap_list2->generate_code_noalias(&expr, NULL);
    }
    expr.expr = mputc(expr.expr, ')');
    return Code::merge_free_expr(str, &expr);
  }

  char *Statement::generate_code_compop(char *str, const char *opname)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    if (comp_op.compref) {
      Value *v_last = comp_op.compref->get_value_refd_last();
      if (v_last->get_valuetype() == Value::V_REFD) {
	// the argument is a simple component reference
	v_last->generate_code_expr_mandatory(&expr);
	expr.expr = mputprintf(expr.expr, ".%s()", opname);
      } else {
	bool refers_to_self = false;
	if (v_last->get_valuetype() == Value::V_EXPR) {
	  // the argument is a special component reference (mtc, self, etc.)
	  switch (v_last->get_optype()) {
	  case Value::OPTYPE_COMP_MTC: {
	    Definition *my_def = my_sb->get_my_def();
	    if (my_def && my_def->get_asstype() == Definition::A_TESTCASE)
	      refers_to_self = true;
	    break; }
	  case Value::OPTYPE_COMP_SELF:
	    refers_to_self = true;
	  default:
	    break;
	  }
	}
	if (refers_to_self) {
	  expr.expr = mputprintf(expr.expr, "TTCN_Runtime::%s_execution()",
	    opname);
	} else {
	  expr.expr = mputprintf(expr.expr, "TTCN_Runtime::%s_component(",
	    opname);
	  v_last->generate_code_expr(&expr);
	  expr.expr = mputc(expr.expr, ')');
	}
      }
    } else {
      // the operation refers to all component
      expr.expr = mputprintf(expr.expr,
	"TTCN_Runtime::%s_component(ALL_COMPREF)", opname);
    }
    return Code::merge_free_expr(str, &expr);
  }

  char *Statement::generate_code_configop(char *str, const char *opname)
  {
    expression_struct expr;
    Code::init_expr(&expr);

    expr.expr = mputprintf(expr.expr, "TTCN_Runtime::%s_port(", opname);
    config_op.compref1->generate_code_expr(&expr);
    expr.expr = mputstr(expr.expr, ", ");
    if (config_op.compref1->get_expr_governor(Type::EXPECTED_DYNAMIC_VALUE)) {
      // the component type is known
      // the name of the referred port can be used
      config_op.portref1->generate_code_portref(&expr, my_sb);
      expr.expr = mputstr(expr.expr, ".get_name()");
    } else {
      // the component type is unknown
      // a simple string shall be formed from the port name and array indices
      generate_code_portref(&expr, config_op.portref1);
    }
    expr.expr = mputstr(expr.expr, ", ");
    config_op.compref2->generate_code_expr(&expr);
    expr.expr = mputstr(expr.expr, ", ");
    if (config_op.compref2->get_expr_governor(Type::EXPECTED_DYNAMIC_VALUE)) {
      // the component type is known
      // the name of the referred port can be used
      config_op.portref2->generate_code_portref(&expr, my_sb);
      expr.expr = mputstr(expr.expr, ".get_name()");
    } else {
      // the component type is unknown
      // a simple string shall be formed from the port name and array indices
      generate_code_portref(&expr, config_op.portref2);
    }
    if (config_op.ap_list != NULL) {
      // handle 'map'/'unmap' parameters
      size_t nof_pars = config_op.ap_list->get_nof_pars();
      string tmp_id = my_sb->get_scope_mod_gen()->get_temporary_id();
      expr.preamble = mputprintf(expr.preamble,
        "Map_Params %s(%u);\n", tmp_id.c_str(), (unsigned int) nof_pars);
      for (size_t i = 0; i < nof_pars; ++i) {
        ActualPar* ap = config_op.ap_list->get_par(i);
        FormalPar* fp = config_op.fp_list->get_fp_byIndex(i);
        bool copy_needed = ap->get_selection() == ActualPar::AP_VALUE;
        expression_struct par_expr;
        Code::init_expr(&par_expr);
        ap->generate_code(&par_expr, copy_needed, fp);
        if (fp->get_asstype() != Definition::A_PAR_VAL_OUT) {
          // set the values of 'in' and 'inout' parameters before the call
          if (par_expr.preamble != NULL) {
            expr.preamble = mputstr(expr.preamble, par_expr.preamble);
          }
          expr.preamble = mputprintf(expr.preamble,
            "%s.set_param(%u, ttcn_to_string(%s));\n",
            tmp_id.c_str(), (unsigned int) i, par_expr.expr);
        }
        else {
          // set empty strings for 'out' parameters
          expr.preamble = mputprintf(expr.preamble,
            "%s.set_param(%u, CHARSTRING(\"\"));\n",
            tmp_id.c_str(), (unsigned int) i);
        }
        if (fp->get_asstype() == Definition::A_PAR_VAL_OUT ||
            fp->get_asstype() == Definition::A_PAR_VAL_INOUT) {
          // store the new values of 'out' and 'inout' parameters after the call
          // note: this only works in single mode, in parallel mode execution
          // will continue after the mapping request, without waiting for a
          // reply that could contain the new values of the parameters
          expr.postamble = mputprintf(expr.postamble,
            "if (%s.get_param(%u).lengthof() > 0) "
            "string_to_ttcn(%s.get_param(%u), %s);\n",
            tmp_id.c_str(), (unsigned int) i,
            tmp_id.c_str(), (unsigned int) i, par_expr.expr);
          if (par_expr.postamble != NULL) {
            expr.postamble = mputstr(expr.postamble, par_expr.postamble);
          }
        }
        Code::free_expr(&par_expr);
      }
      expr.expr = mputprintf(expr.expr, ", %s", tmp_id.c_str());
    }
    else if (statementtype == S_MAP || statementtype == S_UNMAP) {
      // create an empty struct for 'connect' and 'disconnect'
      string tmp_id = my_sb->get_scope_mod_gen()->get_temporary_id();
      expr.preamble = mputprintf(expr.preamble,
        "Map_Params %s(0);\n", tmp_id.c_str());
      expr.expr = mputprintf(expr.expr, ", %s", tmp_id.c_str());
    }
    if (config_op.translate == true) {
      expr.expr = mputstr(expr.expr, ", TRUE");
    }
    expr.expr = mputstr(expr.expr, ")");
    return Code::merge_free_expr(str, &expr);
  }

  char *Statement::generate_code_starttimer(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    timer_op.timerref->generate_code(&expr);
    expr.expr=mputstr(expr.expr, ".start(");
    if(timer_op.value) timer_op.value->generate_code_expr(&expr);
    expr.expr=mputc(expr.expr, ')');
    str=Code::merge_free_expr(str, &expr);
    return str;
  }

  char *Statement::generate_code_stoptimer(char *str)
  {
    if(!timer_op.timerref) str=mputstr(str, "TIMER::all_stop();\n");
    else {
      expression_struct expr;
      Code::init_expr(&expr);
      timer_op.timerref->generate_code(&expr);
      expr.expr=mputstr(expr.expr, ".stop()");
      str=Code::merge_free_expr(str, &expr);
    }
    return str;
  }

  char *Statement::generate_code_setverdict(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    expr.expr=mputstr(expr.expr, "TTCN_Runtime::setverdict(");
    setverdict.verdictval->generate_code_expr(&expr);
    if (setverdict.logargs) {
      expr.expr=mputc(expr.expr, ',');
      expression_struct expr_reason;
      Code::init_expr(&expr_reason);
      setverdict.logargs->generate_code_expr(&expr_reason);
      if (expr_reason.preamble)
        expr.preamble = mputprintf(expr.preamble, "%s;\n",
                                   expr_reason.preamble);
      if (expr_reason.postamble)
        expr.postamble = mputprintf(expr.postamble, "%s;\n",
                                    expr_reason.postamble);
      //TODO: when there is more than 1 reason parameter
      // the resulting string should be extracted into a temporary variable.
      expr.expr = mputprintf(expr.expr, "%s", expr_reason.expr);
      Code::free_expr(&expr_reason);
    }
    expr.expr=mputc(expr.expr, ')');
    str=Code::merge_free_expr(str, &expr);
    return str;
  }

  char *Statement::generate_code_action(char *str)
  {
    str=mputstr(str, "TTCN_Runtime::begin_action();\n");
    if(!logargs) str=mputstr(str, "TTCN_Logger::log_event_str"
                             "(\"<empty action statement>\");\n");
    else str=logargs->generate_code(str);
    str=mputstr(str, "TTCN_Runtime::end_action();\n");
    return str;
  }

  char *Statement::generate_code_testcaseinst(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    Common::Assignment *testcase = testcase_inst.tcref->get_refd_assignment();
    expr.expr = mputprintf(expr.expr, "%s(",
      testcase->get_genname_from_scope(my_sb, "testcase_").c_str());
    ActualParList *t_aplist = testcase_inst.tcref->get_parlist();
    if (t_aplist->get_nof_pars() > 0) {
      t_aplist->generate_code_alias(&expr, testcase->get_FormalParList(),
        0, false);
      expr.expr = mputstr(expr.expr, ", ");
    }
    if (testcase_inst.timerval) {
      expr.expr = mputstr(expr.expr, "TRUE, ");
      testcase_inst.timerval->generate_code_expr(&expr);
      expr.expr = mputc(expr.expr, ')');
    } else expr.expr = mputstr(expr.expr, "FALSE, 0.0)");
    return Code::merge_free_expr(str, &expr);
  }

  char *Statement::generate_code_execute_refd(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    Value *last_v = fau_refd.value->get_value_refd_last();
    if (last_v->get_valuetype() == Value::V_TESTCASE) {
      Common::Assignment *testcase = last_v->get_refd_fat();
      expr.expr = mputprintf(expr.expr, "%s(",
	testcase->get_genname_from_scope(my_sb, "testcase_").c_str());
      execute_refd.ap_list2->generate_code_alias(&expr,
	testcase->get_FormalParList(), 0, false);
    } else {
      execute_refd.value->generate_code_expr_mandatory(&expr);
      expr.expr = mputstr(expr.expr, ".execute(");
      execute_refd.ap_list2->generate_code_alias(&expr, 0, 0, false);
    }
    if (execute_refd.ap_list2->get_nof_pars() > 0)
      expr.expr = mputstr(expr.expr, ", ");
    if (execute_refd.timerval) {
      expr.expr = mputstr(expr.expr, "TRUE, ");
      execute_refd.timerval->generate_code_expr(&expr);
      expr.expr = mputc(expr.expr, ')');
    } else expr.expr = mputstr(expr.expr, "FALSE, 0.0)");
    return Code::merge_free_expr(str,&expr);
  }
  
  char *Statement::generate_code_update(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    Common::Assignment* refd_ass = update_op.ref->get_refd_assignment(false);
    GovernedSimple* refd_obj = static_cast<GovernedSimple*>(
      refd_ass->get_Setting());
    
    // namespace prefix, in case the value/template was declared in another module
    string prefix;
    if (my_sb->get_scope_mod_gen() != refd_obj->get_my_scope()->get_scope_mod_gen()) {
      prefix = refd_obj->get_my_scope()->get_scope_mod_gen()->get_modid().get_name() +
        string("::");
    }
    if (refd_obj->get_err_descr()->has_descr(this)) {
      // the statement has erroneous attributes
      // generate them to the global scope, so they remain active even after the
      // '@update' statement's scope ends
      src_glob_vars = refd_obj->get_err_descr()->generate_code_str(this,
        src_glob_vars, def_glob_vars, refd_obj->get_lhs_name());
      
      // generate the descriptor's initialization code to the local scope, since
      // it may depend on local variables
      str = refd_obj->get_err_descr()->generate_code_init_str(this, str,
        refd_obj->get_lhs_name());
      if (refd_ass->get_FormalParList() != NULL) {
        // a global descriptor pointer is used for parameterized templates, since
        // there is no global constant or template to store the descriptor's
        // address in
        str = mputprintf(str, "%s%s_err_descr_ptr = &%s_%lu_err_descr;\n",
          prefix.c_str(), refd_obj->get_lhs_name().c_str(),
          refd_obj->get_lhs_name().c_str(),
          static_cast<unsigned long>( refd_obj->get_err_descr()->get_descr_index(this) ));
      }
      else {
        // store the descriptor's address in the constant/template
        str = mputprintf(str, "%s%s.set_err_descr(&%s_%lu_err_descr);\n",
          prefix.c_str(), refd_obj->get_lhs_name().c_str(),
          refd_obj->get_lhs_name().c_str(),
          static_cast<unsigned long>( refd_obj->get_err_descr()->get_descr_index(this) ));
      }
    }
    else {
      // remove the previously stored descriptor's address (if any)
      if (refd_ass->get_FormalParList() != NULL) {
        str = mputprintf(str, "%s%s_err_descr_ptr = NULL;\n", prefix.c_str(),
          refd_obj->get_lhs_name().c_str());
      }
      else {
        str = mputprintf(str, "%s%s.set_err_descr(NULL);\n", prefix.c_str(),
          refd_obj->get_lhs_name().c_str());
      }
    }
    return str;
  }
  
  char* Statement::generate_code_setstate(char *str) {
    expression_struct expr;
    Code::init_expr(&expr);
    expr.expr = mputstr(expr.expr, "TTCN_Runtime::set_port_state(");
    if (!setstate_op.val->is_unfoldable()) {
      expr.expr = mputprintf(expr.expr, "%i", static_cast<int>( setstate_op.val->get_val_Int()->get_val() ));
    } else {
      setstate_op.val->generate_code_expr(&expr);
    }
    
    expr.expr = mputstr(expr.expr, ", ");
    if (setstate_op.ti != NULL) {
      expr.expr = mputstr(expr.expr, "(TTCN_Logger::begin_event(TTCN_Logger::PORTEVENT_SETSTATE, TRUE), ");
      setstate_op.ti->generate_code(&expr);
      expr.expr = mputstr(expr.expr, ".log(), TTCN_Logger::end_event_log2str()), FALSE);\n");
    } else {
      expr.expr = mputstr(expr.expr, "\"\", FALSE);\n");
    }
    return Code::merge_free_expr(str, &expr);
  }
  
  char* Statement::generate_code_setencode(char* str)
  {
    if (legacy_codec_handling) {
      FATAL_ERROR("Statement::generate_code_setencode");
    }
    if (setencode_op.type->get_type_w_coding_table()->get_coding_table().size() == 1) {
      // the 'setencode' statement is ignored if the type has only one encoding
      return str;
    }
    expression_struct expr;
    Code::init_expr(&expr);
    setencode_op.encoding->generate_code_expr(&expr);
    if (expr.preamble != NULL) {
      str = mputstr(str, expr.preamble);
    }
    str = mputprintf(str, "%s_default_coding = %s;\n",
      setencode_op.type->get_genname_default_coding(my_sb).c_str(), expr.expr);
    if (expr.postamble != NULL) {
      str = mputstr(str, expr.postamble);
    }
    Code::free_expr(&expr);
    return str;
  }
  
  char* Statement::generate_code_raise_oop(char* str)
  {
    expression_struct_t expr;
    Code::init_expr(&expr);
    raise_op.v->generate_code_expr(&expr);
    if (expr.preamble != NULL) {
      str = mputstr(str, expr.preamble);
    }
    Common::Type* exc_type = raise_op.v->get_expr_governor(Common::Type::EXPECTED_DYNAMIC_VALUE);
    Common::Type* exc_type_last = exc_type->get_type_refd_last();
    string exc_type_str = exc_type->get_genname_value(my_sb);
    if (exc_type_last->get_typetype() == Common::Type::T_CLASS) {
      ClassTypeBody* class_ = exc_type_last->get_class_type_body();
      str = mputprintf(str, "throw CLASS_EXCEPTION<%s>(new %s(%s));\n",
        class_->is_built_in() ? "OBJECT" : exc_type_last->get_genname_own(my_sb).c_str(),
        exc_type_str.c_str(), expr.expr);
    }
    else {
      str = mputprintf(str, "throw NON_CLASS_EXCEPTION< %s >(new %s(%s), \"%s\");\n",
        exc_type_str.c_str(), exc_type_str.c_str(), expr.expr, exc_type->get_exception_name().c_str());
    }
    if (expr.postamble != NULL) {
      str = mputstr(str, expr.postamble);
    }
    Code::free_expr(&expr);
    return str;
  }

  void Statement::generate_code_expr_receive(expression_struct *expr,
    const char *opname)
  {
    if (port_op.portref || port_op.translate) {
      if (!port_op.translate) {
        // The operation refers to a specific port.
        port_op.portref->generate_code(expr);
        expr->expr = mputprintf(expr->expr, ".%s(", opname);
      } else {
        // No need for a reference because the send will be called
        // on *this in the c++ code.
        expr->expr = mputprintf(expr->expr, "%s(", opname);
      }
      if (port_op.r.rcvpar) {
        // The receive parameter is present.
        bool has_decoded_redirect = port_op.r.redirect.value != NULL &&
          port_op.r.redirect.value->has_decoded_modifier();
        port_op.r.rcvpar->generate_code(expr, TR_NONE, has_decoded_redirect);
        expr->expr = mputstr(expr->expr, ", ");
        if (port_op.r.redirect.value) {
          // Value redirect is also present.
          port_op.r.redirect.value->generate_code(expr, port_op.r.rcvpar);
        } else expr->expr = mputstr(expr->expr, "NULL");
        expr->expr = mputstr(expr->expr, ", ");
      }
    } else {
      // the operation refers to any port
      expr->expr = mputprintf(expr->expr, "PORT::any_%s(", opname);
    }
    generate_code_expr_fromclause(expr);
    expr->expr = mputstr(expr->expr, ", ");
    generate_code_expr_senderredirect(expr);
    expr->expr = mputstr(expr->expr, ", ");
    generate_code_expr_timestamp_redirect(expr, false);
    if (port_op.portref || port_op.translate) {
      expr->expr = mputstr(expr->expr, ", ");
      if (port_op.r.redirect.index != NULL) {
        generate_code_index_redirect(expr, port_op.r.redirect.index, my_sb);
      }
      else {
        expr->expr = mputstr(expr->expr, "NULL");
      }
    }
    expr->expr = mputc(expr->expr, ')');
  }

  void Statement::generate_code_expr_getcall(expression_struct *expr,
    const char *opname)
  {
    if (port_op.portref) {
      // the operation refers to a specific port
      port_op.portref->generate_code(expr);
      expr->expr = mputprintf(expr->expr, ".%s(", opname);
      if (port_op.r.rcvpar) {
        bool has_decoded_redirect = port_op.r.redirect.param != NULL &&
          port_op.r.redirect.param->has_decoded_modifier();
	// the signature template is present
        port_op.r.rcvpar->generate_code(expr, TR_NONE, has_decoded_redirect);
	expr->expr = mputstr(expr->expr, ", ");
	generate_code_expr_fromclause(expr);
	// a temporary object is needed for parameter redirect
	Type *signature = port_op.r.rcvpar->get_Template()->get_my_governor();
  if (has_decoded_redirect) {
    // a new redirect class (inheriting the old one) is needed if any of the
    // redirects have the '@decoded' modifier
    string tmp_id = my_sb->get_scope_mod_gen()->get_temporary_id();
    expr->preamble = port_op.r.redirect.param->generate_code_decoded(
      expr->preamble, port_op.r.rcvpar, tmp_id.c_str(), false);
    expr->expr = mputprintf(expr->expr, ", %s_call_redirect_%s(",
      signature->get_genname_value(signature->get_type_refd_last()->get_my_scope()
      ).c_str(), tmp_id.c_str());
  }
  else {
    expr->expr = mputprintf(expr->expr, ", %s_call_redirect(",
      signature->get_genname_value(my_sb).c_str());
  }
	if (port_op.r.redirect.param)
	  port_op.r.redirect.param->generate_code(expr, port_op.r.rcvpar, false);
	expr->expr = mputstr(expr->expr, "), ");
	generate_code_expr_senderredirect(expr);
      } else {
	// the signature parameter is not present
	generate_code_expr_fromclause(expr);
	expr->expr = mputstr(expr->expr, ", ");
	generate_code_expr_senderredirect(expr);
      }
      expr->expr = mputstr(expr->expr, ", ");
      generate_code_expr_timestamp_redirect(expr, false);
      expr->expr = mputstr(expr->expr, ", ");
      if (port_op.r.redirect.index != NULL) {
        generate_code_index_redirect(expr, port_op.r.redirect.index, my_sb);
      }
      else {
        expr->expr = mputstr(expr->expr, "NULL");
      }
    } else {
      // the operation refers to any port
      expr->expr = mputprintf(expr->expr, "PORT::any_%s(", opname);
      generate_code_expr_fromclause(expr);
      expr->expr = mputstr(expr->expr, ", ");
      generate_code_expr_senderredirect(expr);
      expr->expr = mputstr(expr->expr, ", ");
      generate_code_expr_timestamp_redirect(expr, false);
    }
    expr->expr=mputc(expr->expr, ')');
  }

  void Statement::generate_code_expr_getreply(expression_struct *expr,
    const char *opname)
  {
    if (port_op.portref) {
      // the operation refers to a specific port
      port_op.portref->generate_code(expr);
      expr->expr = mputprintf(expr->expr, ".%s(", opname);
      if (port_op.r.rcvpar) {
        bool has_decoded_param_redirect = port_op.r.redirect.param != NULL &&
          port_op.r.redirect.param->has_decoded_modifier();
	// the signature template is present
        port_op.r.rcvpar->generate_code(expr, TR_NONE, has_decoded_param_redirect);
	Type *signature = port_op.r.rcvpar->get_Template()->get_my_governor();
	Type *return_type =
	  signature->get_type_refd_last()->get_signature_return_type();
	if (return_type) {
	  expr->expr = mputstr(expr->expr, ".set_value_template(");
	  if (port_op.r.getreply_valuematch) {
	    // the value match is also present
      bool has_decoded_value_redirect = port_op.r.redirect.value != NULL &&
        port_op.r.redirect.value->has_decoded_modifier();
	    port_op.r.getreply_valuematch->generate_code(expr, TR_NONE,
        has_decoded_value_redirect);
	  } else {
	    // the value match is not present
	    // we must substitute it with ? in the signature template
	    expr->expr = mputprintf(expr->expr, "%s(ANY_VALUE)",
	      return_type->get_genname_template(my_sb).c_str());
	  }
	  expr->expr = mputc(expr->expr, ')');
	}
	expr->expr = mputstr(expr->expr, ", ");
	generate_code_expr_fromclause(expr);
	// a temporary object is needed for value and parameter redirect
  if (has_decoded_param_redirect) {
    // a new redirect class (inheriting the old one) is needed if any of the
    // redirects have the '@decoded' modifier
    string tmp_id = my_sb->get_scope_mod_gen()->get_temporary_id();
    expr->preamble = port_op.r.redirect.param->generate_code_decoded(
      expr->preamble, port_op.r.rcvpar, tmp_id.c_str(), true);
    expr->expr = mputprintf(expr->expr, ", %s_reply_redirect_%s(",
      signature->get_genname_value(signature->get_type_refd_last()->get_my_scope()
      ).c_str(), tmp_id.c_str());
  }
  else {
    expr->expr = mputprintf(expr->expr, ", %s_reply_redirect(",
      signature->get_genname_value(my_sb).c_str());
  }
	if (return_type) {
	  // the first argument of the constructor must contain
	  // the value redirect
	  if (port_op.r.redirect.value) {
	    port_op.r.redirect.value->generate_code(expr, port_op.r.getreply_valuematch);
	  } else expr->expr = mputstr(expr->expr, "NULL");
	  if (port_op.r.redirect.param) expr->expr = mputstr(expr->expr, ", ");
	}
	if (port_op.r.redirect.param)
	  port_op.r.redirect.param->generate_code(expr, port_op.r.rcvpar, true);
	expr->expr = mputstr(expr->expr, "), ");
	generate_code_expr_senderredirect(expr);
      } else {
	// the signature template is not present
	generate_code_expr_fromclause(expr);
	expr->expr = mputstr(expr->expr, ", ");
	generate_code_expr_senderredirect(expr);
      }
      expr->expr = mputstr(expr->expr, ", ");
      generate_code_expr_timestamp_redirect(expr, false);
      expr->expr = mputstr(expr->expr, ", ");
      if (port_op.r.redirect.index != NULL) {
        generate_code_index_redirect(expr, port_op.r.redirect.index, my_sb);
      }
      else {
        expr->expr = mputstr(expr->expr, "NULL");
      }
    } else {
      // the operation refers to any port
      expr->expr = mputprintf(expr->expr, "PORT::any_%s(", opname);
      generate_code_expr_fromclause(expr);
      expr->expr = mputstr(expr->expr, ", ");
      generate_code_expr_senderredirect(expr);
      expr->expr = mputstr(expr->expr, ", ");
      generate_code_expr_timestamp_redirect(expr, false);
    }
    expr->expr = mputc(expr->expr, ')');
  }

  void Statement::generate_code_expr_catch(expression_struct *expr)
  {
    if (port_op.portref) {
      // the operation refers to a specific port
      if (port_op.r.ctch.timeout) {
	// the operation catches the timeout exception
	expr->expr = mputstr(expr->expr, "call_timer.timeout()");
	return;
      }
      port_op.portref->generate_code(expr);
      expr->expr = mputprintf(expr->expr, ".%s(",
	statementtype == S_CHECK_CATCH ? "check_catch" : "get_exception");
      if (port_op.r.ctch.signature_ref) {
	// the signature reference and the exception template is present
	expr->expr = mputprintf(expr->expr, "%s_exception_template(",
	  port_op.r.ctch.signature->get_genname_value(my_sb).c_str());
  bool has_decoded_redirect = port_op.r.redirect.value != NULL &&
    port_op.r.redirect.value->has_decoded_modifier();
	port_op.r.rcvpar->generate_code(expr, TR_NONE, has_decoded_redirect);
	expr->expr = mputstr(expr->expr, ", ");
	if (port_op.r.redirect.value) {
	  // value redirect is also present
	  port_op.r.redirect.value->generate_code(expr, port_op.r.rcvpar);
	} else expr->expr = mputstr(expr->expr, "NULL");
	expr->expr = mputstr(expr->expr, "), ");
      }
    } else {
      // the operation refers to any port
      expr->expr = mputprintf(expr->expr, "PORT::%s(",
	statementtype == S_CHECK_CATCH ? "any_check_catch" : "any_catch");
    }
    generate_code_expr_fromclause(expr);
    expr->expr = mputstr(expr->expr, ", ");
    generate_code_expr_senderredirect(expr);
    expr->expr = mputstr(expr->expr, ", ");
    generate_code_expr_timestamp_redirect(expr, false);
    if (port_op.portref) {
      expr->expr = mputstr(expr->expr, ", ");
      if (port_op.r.redirect.index != NULL) {
        generate_code_index_redirect(expr, port_op.r.redirect.index, my_sb);
      }
      else {
        expr->expr = mputstr(expr->expr, "NULL");
      }
    }
    expr->expr = mputc(expr->expr, ')');
  }

  void Statement::generate_code_expr_check(expression_struct *expr)
  {
    if (port_op.portref) {
      // the operation refers to a specific port
      port_op.portref->generate_code(expr);
      expr->expr = mputstr(expr->expr, ".check");
    } else {
      // the operation refers to any port
      expr->expr = mputstr(expr->expr, "PORT::any_check");
    }
    expr->expr = mputc(expr->expr, '(');
    generate_code_expr_fromclause(expr);
    expr->expr = mputstr(expr->expr, ", ");
    generate_code_expr_senderredirect(expr);
    expr->expr = mputstr(expr->expr, ", ");
    generate_code_expr_timestamp_redirect(expr, false);
    if (port_op.portref) {
      expr->expr = mputstr(expr->expr, ", ");
      if (port_op.r.redirect.index != NULL) {
        generate_code_index_redirect(expr, port_op.r.redirect.index, my_sb);
      }
      else {
        expr->expr = mputstr(expr->expr, "NULL");
      }
    }
    expr->expr = mputc(expr->expr, ')');
  }

  void Statement::generate_code_expr_done(expression_struct *expr)
  {
    if (comp_op.compref) {
      if (comp_op.donereturn.donematch) {
        // value returning done
        // figure out what type the done() function belongs to
        Type *t = comp_op.donereturn.donematch
          ->get_expr_governor(Type::EXPECTED_TEMPLATE);
        if (!t) FATAL_ERROR("Statement::generate_code_expr_done()");
        while (t->is_ref() && !t->has_done_attribute())
          t = t->get_type_refd();
        if (!t->has_done_attribute())
          FATAL_ERROR("Statement::generate_code_expr_done()");
        // determine whether the done() function is in the same module
        Common::Module *t_mod = t->get_my_scope()->get_scope_mod_gen();
        if (t_mod != my_sb->get_scope_mod_gen()) {
          expr->expr = mputprintf(expr->expr, "%s::",
            t_mod->get_modid().get_name().c_str());
        }
        expr->expr = mputstr(expr->expr, "done(");
        comp_op.compref->generate_code_expr(expr);
        expr->expr = mputstr(expr->expr, ", ");
        bool has_decoded_redirect = comp_op.donereturn.redirect != NULL &&
          comp_op.donereturn.redirect->has_decoded_modifier();
        comp_op.donereturn.donematch->generate_code(expr, TR_NONE, has_decoded_redirect);
        expr->expr = mputstr(expr->expr, ", ");
      } else {
        // simple done
        comp_op.compref->generate_code_expr_mandatory(expr);
        expr->expr = mputstr(expr->expr, ".done(");        
      }
      if (comp_op.donereturn.redirect != NULL) {
        // value redirect is present
        comp_op.donereturn.redirect->generate_code(expr, comp_op.donereturn.donematch);
      } else {
        // value redirect is omitted
        expr->expr = mputstr(expr->expr, "NULL");
      }
      expr->expr = mputstr(expr->expr, ", ");
      if (comp_op.index_redirect != NULL) {
        generate_code_index_redirect(expr, comp_op.index_redirect, my_sb);
      }
      else {
        expr->expr = mputstr(expr->expr, "NULL");
      }
      expr->expr = mputc(expr->expr, ')');
    } else if (comp_op.any_or_all == C_ANY) {
      // any component.done
      expr->expr = mputstr(expr->expr,
	"TTCN_Runtime::component_done(ANY_COMPREF)");
    } else {
      // all component.done
      expr->expr = mputstr(expr->expr,
	"TTCN_Runtime::component_done(ALL_COMPREF)");
    }
  }

  void Statement::generate_code_expr_killed(expression_struct *expr)
  {
    if (comp_op.compref) {
      // compref.killed
      comp_op.compref->generate_code_expr_mandatory(expr);
      expr->expr = mputstr(expr->expr, ".killed(");
      if (comp_op.index_redirect != NULL) {
        generate_code_index_redirect(expr, comp_op.index_redirect, my_sb);
      }
      else {
        expr->expr = mputstr(expr->expr, "NULL");
      }
      expr->expr = mputc(expr->expr, ')');
    } else if (comp_op.any_or_all == C_ANY) {
      // any component.killed
      expr->expr = mputstr(expr->expr,
	"TTCN_Runtime::component_killed(ANY_COMPREF)");
    } else {
      // all component.killed
      expr->expr = mputstr(expr->expr,
	"TTCN_Runtime::component_killed(ALL_COMPREF)");
    }
  }

  void Statement::generate_code_expr_timeout(expression_struct *expr)
  {
    if (timer_op.timerref) {
      timer_op.timerref->generate_code(expr);
      expr->expr=mputstr(expr->expr, ".timeout(");
      if (timer_op.index_redirect != NULL) {
        generate_code_index_redirect(expr, timer_op.index_redirect, my_sb);
      }
      else {
        expr->expr = mputstr(expr->expr, "NULL");
      }
      expr->expr = mputc(expr->expr, ')');
    } else expr->expr = mputstr(expr->expr, "TIMER::any_timeout()");
  }

  void Statement::generate_code_expr_sendpar(expression_struct *expr)
  {
    Template *templ_body = port_op.s.sendpar->get_Template();
    if (!port_op.s.sendpar->get_DerivedRef() &&
	templ_body->get_templatetype() == Template::SPECIFIC_VALUE) {
      // the send parameter is a value: optimization is possible
      Value *t_val = templ_body->get_specific_value();
      bool cast_needed = t_val->explicit_cast_needed();
      if (cast_needed) {
	// the ambiguous C++ expression is converted to the value class
	expr->expr = mputprintf(expr->expr, "%s(",
	  t_val->get_my_governor()->get_genname_value(my_sb).c_str());
      }
      t_val->generate_code_expr_mandatory(expr);
      if (cast_needed) expr->expr = mputc(expr->expr, ')');
    } else {
      // the send parameter is a real template: optimization is not possible
      port_op.s.sendpar->generate_code(expr);
    }
  }

  void Statement::generate_code_expr_fromclause(expression_struct *expr)
  {
    if (port_op.r.fromclause) {
      // the from clause is present: trivial case
      port_op.r.fromclause->generate_code(expr);
    } else if (port_op.r.redirect.sender) {
      // from clause is omitted, but sender redirect is present
      Type *t_var_type = port_op.r.redirect.sender->chk_variable_ref();
      if (!t_var_type)
	FATAL_ERROR("Statement::generate_code_expr_fromclause()");
      if (t_var_type->get_type_refd_last()->get_typetype() ==
	  Type::T_COMPONENT) {
	// the variable can store a component reference
	expr->expr = mputstr(expr->expr, "any_compref");
      } else {
	// the variable can store an address value
	expr->expr = mputprintf(expr->expr, "%s(ANY_VALUE)",
	  t_var_type->get_genname_template(my_sb).c_str());
      }
    } else {
      // neither from clause nor sender redirect is present
      // the operation cannot refer to address type
      expr->expr = mputstr(expr->expr, "any_compref");
    }
  }

  void Statement::generate_code_expr_senderredirect(expression_struct *expr)
  {
    if (port_op.r.redirect.sender) {
      expr->expr = mputstr(expr->expr, "&(");
      port_op.r.redirect.sender->generate_code(expr);
      expr->expr = mputc(expr->expr, ')');
    } else expr->expr = mputstr(expr->expr, "NULL");
  }
  
  void Statement::generate_code_expr_timestamp_redirect(expression_struct* expr,
                                                        bool outgoing)
  {
    Reference* tr = outgoing ? port_op.s.timestampredirect :
      port_op.r.redirect.timestamp;
    if (tr != NULL) {
      expr->expr = mputstr(expr->expr, "&(");
      tr->generate_code(expr);
      expr->expr = mputstr(expr->expr, ")");
    }
    else {
      expr->expr = mputstr(expr->expr, "NULL");
    }
  }

  void Statement::generate_code_portref(expression_struct *expr,
    Reference *p_ref)
  {
    // make a backup of the current expression
    char *expr_backup = expr->expr;
    // build the equivalent of p_ref in expr->expr
    expr->expr = mprintf("\"%s\"", p_ref->get_id()->get_dispname().c_str());
    FieldOrArrayRefs *t_subrefs = p_ref->get_subrefs();
    if (t_subrefs) {
      // array indices are present
      for (size_t i = 0; i < t_subrefs->get_nof_refs(); i++) {
	FieldOrArrayRef *t_ref = t_subrefs->get_ref(i);
	if (t_ref->get_type() != FieldOrArrayRef::ARRAY_REF)
	  FATAL_ERROR("Statement::generate_code_portref()");
	// transform expr->expr: XXXX -> get_port_name(XXXX, index)
	char *tmp = expr->expr;
	expr->expr = mcopystr("get_port_name(");
	expr->expr = mputstr(expr->expr, tmp);
	Free(tmp);
	expr->expr = mputstr(expr->expr, ", ");
	t_ref->get_val()->generate_code_expr(expr);
	expr->expr = mputc(expr->expr, ')');
      }
    }
    // now expr->expr contains the equivalent of p_ref
    // append it to the original expression and restore the result
    expr_backup = mputstr(expr_backup, expr->expr);
    Free(expr->expr);
    expr->expr = expr_backup;
  }
  
  void Statement::generate_code_index_redirect(expression_struct* expr,
                                               Reference* p_ref,
                                               Scope* p_scope)
  {
    // A new class is generated into the expression's preamble (inheriting the
    // Index_Redirect class and implementing its virtual function).
    // One instance of the class is created and its address is generated into 
    // the expression proper (expr->expr).
    expression_struct ref_expr;
    Code::init_expr(&ref_expr);
    p_ref->generate_code(&ref_expr);
    if (ref_expr.preamble != NULL) {
      expr->preamble = mputstr(expr->preamble, ref_expr.preamble);
    }
    string tmp_id_class = p_scope->get_scope_mod_gen()->get_temporary_id();
    string tmp_id_var = p_scope->get_scope_mod_gen()->get_temporary_id();
    Type* ref_type = p_ref->chk_variable_ref();
    expr->preamble = mputprintf(expr->preamble,
      "class Index_Redirect_%s : public Index_Redirect {\n"
      "%s* ptr;\n"
      "public:\n"
      "Index_Redirect_%s(%s* par): Index_Redirect(), ptr(par) { }\n"
      "void add_index(int p_index) { ",
      tmp_id_class.c_str(), ref_type->get_genname_value(p_scope).c_str(),
      tmp_id_class.c_str(), ref_type->get_genname_value(p_scope).c_str());
    switch (ref_type->get_type_refd_last()->get_typetype_ttcn3()) {
    case Type::T_INT:
      expr->preamble = mputstr(expr->preamble, "*ptr");
      break;
    case Type::T_SEQOF:
      expr->preamble = mputstr(expr->preamble, "(*ptr)[pos]");
      break;
    case Type::T_ARRAY: {
      ArrayDimension* dim = ref_type->get_type_refd_last()->get_dimension();
      int offset = dim->get_offset();
      char* offset_str = offset < 0 ? mprintf(" - %d", -offset) :
        (offset > 0 ? mprintf(" + %d", offset) : memptystr());
      expr->preamble = mputprintf(expr->preamble, "(*ptr)[pos%s]", offset_str);
      Free(offset_str);
      break; }
    default:
      FATAL_ERROR("Statement::generate_code_index_redirect");
    }
    expr->preamble = mputprintf(expr->preamble,
      " = p_index; }\n"
      "};\n"
      "Index_Redirect_%s %s(&(%s));\n",
      tmp_id_class.c_str(), tmp_id_var.c_str(),ref_expr.expr);
    if (ref_expr.postamble != NULL) {
      expr->preamble = mputstr(expr->preamble, ref_expr.postamble);
    }
    Code::free_expr(&ref_expr);
    expr->expr = mputprintf(expr->expr, "&%s", tmp_id_var.c_str());
  }
  
  void Statement::set_parent_path(WithAttribPath* p_path) {
    switch (statementtype) {
        case S_DEF:
          def->set_parent_path(p_path);
          break;
        case S_BLOCK:
          block->set_parent_path(p_path);
          break;
        case S_IF:
          if_stmt.ics->set_parent_path(p_path);
          if (if_stmt.elseblock)
            if_stmt.elseblock->set_parent_path(p_path);
          break;
        case S_SELECT:
          select.scs->set_parent_path(p_path);
          break;
        case S_SELECTUNION:
          select_union.sus->set_parent_path(p_path);
          break;
        case S_SELECT_CLASS:
          select_class.sccs->set_parent_path(p_path);
          break;
        case S_FOR:
          loop.block->set_parent_path(p_path);
          break;
        case S_WHILE:
        case S_DOWHILE:
          loop.block->set_parent_path(p_path);
          break;
        case S_ASSIGNMENT:
        case S_LOG:
        case S_ACTION:
        case S_LABEL:
        case S_GOTO:
        case S_ERROR:
        case S_BREAK:
        case S_CONTINUE:
        case S_STOP_EXEC:
        case S_STOP_TESTCASE:
        case S_REPEAT:
        case S_START_UNDEF:
        case S_STOP_UNDEF:
        case S_UNKNOWN_INSTANCE:
        case S_FUNCTION_INSTANCE:
        case S_ALTSTEP_INSTANCE:
        case S_ACTIVATE:
        case S_ALT:
        case S_INTERLEAVE:
        case S_RETURN:
        case S_DEACTIVATE:
        case S_SEND:
        case S_CALL:
        case S_REPLY:
        case S_RAISE:
        case S_RECEIVE:
        case S_CHECK_RECEIVE:
        case S_TRIGGER:
        case S_GETCALL:
        case S_CHECK_GETCALL:
        case S_GETREPLY:
        case S_CHECK_GETREPLY:
        case S_CATCH:
        case S_CHECK_CATCH:
        case S_CHECK:
        case S_CLEAR:
        case S_START_PORT:
        case S_STOP_PORT:
        case S_HALT:
        case S_START_COMP:
        case S_START_COMP_REFD:
        case S_STOP_COMP:
        case S_KILL:
        case S_KILLED:
        case S_DONE:
        case S_CONNECT:
        case S_MAP:
        case S_DISCONNECT:
        case S_UNMAP:
        case S_START_TIMER:
        case S_STOP_TIMER:
        case S_TIMEOUT:
        case S_SETVERDICT:
        case S_TESTCASE_INSTANCE:
        case S_TESTCASE_INSTANCE_REFD:
        case S_ACTIVATE_REFD:
        case S_UNKNOWN_INVOKED:
        case S_FUNCTION_INVOKED:
        case S_ALTSTEP_INVOKED:
        case S_STRING2TTCN:
        case S_START_PROFILER:
        case S_STOP_PROFILER:
        case S_INT2ENUM:
        case S_UPDATE:
        case S_SETSTATE:
        case S_SETENCODE:
        case S_RAISE_OOP:
          break;
        default:
          FATAL_ERROR("Statement::set_parent_path()");
        }
  }

  // =================================
  // ===== Assignment
  // =================================

  Assignment::Assignment(Reference *p_ref, Template *p_templ)
    : asstype(ASS_UNKNOWN), ref(p_ref), templ(p_templ), self_ref(false),
      template_restriction(TR_NONE), gen_restriction_check(false),
      in_constructor(false)
  {
    if(!ref || !templ) FATAL_ERROR("Ttcn::Assignment::Assignment");
  }

  Assignment::Assignment(Reference *p_ref, Value *p_val)
    : asstype(ASS_VAR), ref(p_ref), val(p_val), self_ref(false),
      template_restriction(TR_NONE), gen_restriction_check(false),
      in_constructor(false)
  {
    if(!ref || !val) FATAL_ERROR("Ttcn::Assignment::Assignment");
  }

  Assignment::~Assignment()
  {
    switch(asstype) {
    case ASS_UNKNOWN:
    case ASS_TEMPLATE:
      delete ref;
      delete templ;
      break;
    case ASS_VAR:
      delete ref;
      delete val;
      break;
    case ASS_ERROR:
      break;
    default:
      FATAL_ERROR("Ttcn::Assignment::~Assignment()");
    } // switch
  }

  Assignment *Assignment::clone() const
  {
    FATAL_ERROR("Assignment::clone");
  }

  void Assignment::set_my_scope(Scope *p_scope)
  {
    switch(asstype) {
    case ASS_UNKNOWN:
    case ASS_TEMPLATE:
      ref->set_my_scope(p_scope);
      templ->set_my_scope(p_scope);
      break;
    case ASS_VAR:
      ref->set_my_scope(p_scope);
      val->set_my_scope(p_scope);
      break;
    case ASS_ERROR:
      break;
    default:
      FATAL_ERROR("Ttcn::Assignment::set_my_scope()");
    } // switch
  }

  void Assignment::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    switch(asstype) {
    case ASS_UNKNOWN:
    case ASS_TEMPLATE:
      ref->set_fullname(p_fullname);
      templ->set_fullname(p_fullname);
      break;
    case ASS_VAR:
      ref->set_fullname(p_fullname);
      val->set_fullname(p_fullname);
      break;
    case ASS_ERROR:
      break;
    default:
      FATAL_ERROR("Ttcn::Assignment::set_fullname()");
    } // switch
  }

  void Assignment::dump(unsigned int level) const
  {
    // warning, ref is not always set (e.g. ASS_ERROR)
    switch (asstype) {
    case ASS_VAR:
      ref->dump(level+1);
      val->dump(level+1);
      break;

    case ASS_ERROR:
      DEBUG(level, "*** ERROR ***");
      break;

    case ASS_UNKNOWN:
      DEBUG(level, "*** UNKNOWN ***");
      break;

    case ASS_TEMPLATE:
      ref->dump(level+1);
      templ->dump(level+1);
      break;
    }
  }

  void Assignment::chk_unknown_ass()
  {
    Common::Assignment *t_ass = ref->get_refd_assignment_last();
    if (!t_ass) goto error;
    switch (t_ass->get_asstype()) {
    case Common::Assignment::A_ERROR:
      goto error;
    case Common::Assignment::A_PAR_VAL_IN:
      t_ass->use_as_lvalue(*ref);
      // no break
    case Common::Assignment::A_CONST:
      if (t_ass->get_asstype() == Common::Assignment::A_CONST &&
          !in_constructor) {
        ref->error("Reference to a variable or template variable was expected "
          "instead of %s", t_ass->get_description().c_str());
        goto error;
      }
      // no break
    case Common::Assignment::A_VAR:
    case Common::Assignment::A_EXCEPTION:
    case Common::Assignment::A_PAR_VAL_OUT:
    case Common::Assignment::A_PAR_VAL_INOUT:
      if (templ->is_Value()) {
	Value *t_val = templ->get_Value();
	delete templ;
	val = t_val;
	asstype = ASS_VAR;
	chk_var_ass();
      } else {
	templ->error("A template body with matching symbols cannot be"
                     " assigned to a variable");
	goto error;
      }
      break;
    case Common::Assignment::A_PAR_TEMPL_IN:
      t_ass->use_as_lvalue(*ref);
      // no break
    case Common::Assignment::A_TEMPLATE:
      if (t_ass->get_asstype() == Common::Assignment::A_TEMPLATE &&
          !in_constructor) {
        ref->error("Reference to a variable or template variable was expected "
          "instead of %s", t_ass->get_description().c_str());
        goto error;
      }
      // no break
    case Common::Assignment::A_VAR_TEMPLATE: {
      Type::typetype_t tt = t_ass->get_Type()->get_type_refd_last()->get_typetype();
      switch (tt) {
        case Type::T_BSTR:
        case Type::T_BSTR_A:
        case Type::T_HSTR:
        case Type::T_OSTR:
        case Type::T_CSTR:
        case Type::T_USTR:
        case Type::T_UTF8STRING:
        case Type::T_NUMERICSTRING:
        case Type::T_PRINTABLESTRING:
        case Type::T_TELETEXSTRING:
        case Type::T_VIDEOTEXSTRING:
        case Type::T_IA5STRING:
        case Type::T_GRAPHICSTRING:
        case Type::T_VISIBLESTRING:
        case Type::T_GENERALSTRING:
        case Type::T_UNIVERSALSTRING:
        case Type::T_BMPSTRING:
        case Type::T_UTCTIME:
        case Type::T_GENERALIZEDTIME:
        case Type::T_OBJECTDESCRIPTOR: {
          Ttcn::FieldOrArrayRefs *subrefs = ref->get_subrefs();
          if (!subrefs) break;
          size_t nof_subrefs = subrefs->get_nof_refs();
          if (nof_subrefs > 0) {
            Ttcn::FieldOrArrayRef *last_ref = subrefs
              ->get_ref(nof_subrefs - 1);
            if (last_ref->get_type() == Ttcn::FieldOrArrayRef::ARRAY_REF) {
              if (!templ->is_Value()) {
                templ->error("A template body with matching symbols cannot be "
                             "assigned to an element of a template variable");
                goto error;
              }
            }
          }
          break; }
        default:
          break;
      }
    }
    case Common::Assignment::A_PAR_TEMPL_OUT:
    case Common::Assignment::A_PAR_TEMPL_INOUT:
      asstype = ASS_TEMPLATE;
      chk_template_ass();
      break;
    default:
      ref->error("Reference to a variable or template variable was expected "
                 "instead of %s", t_ass->get_description().c_str());
      goto error;
    }
    return;
  error:
    delete ref;
    delete templ;
    asstype = ASS_ERROR;
    return;
  }

  void Assignment::chk_var_ass()
  {
    Common::Assignment *lhs = ref->get_refd_assignment();
    Type *var_type = lhs->get_Type();
    FieldOrArrayRefs *subrefs = ref->get_subrefs();
    Type *type =
      var_type->get_field_type(subrefs, Type::EXPECTED_DYNAMIC_VALUE);
    if (!type) goto error;
    val->set_my_governor(type);
    type->chk_this_value_ref(val);
    if (val->get_value_refd_last()->get_valuetype() == Value::V_OMIT) {
      Identifier *field_id = 0;
      if (subrefs) field_id = subrefs->remove_last_field();
      if (!field_id) {
        val->error("Omit value can be assigned to an optional field of "
                   "a record or set value only");
        goto error;
      }
      Type *base_type = var_type
        ->get_field_type(subrefs, Type::EXPECTED_DYNAMIC_VALUE);
      // Putting field_id back to subrefs.
      subrefs->add(new FieldOrArrayRef(field_id));
      base_type = base_type->get_type_refd_last();
      switch (base_type->get_typetype()) {
      case Type::T_ERROR:
        goto error;
      case Type::T_SEQ_A:
      case Type::T_SEQ_T:
      case Type::T_SET_A:
      case Type::T_SET_T:
        break;
      default:
        val->error("Omit value can be assigned to an optional field of "
                   "a record or set value only");
        goto error;
      }
      if (!base_type->get_comp_byName(*field_id)->get_is_optional()) {
        val->error("Assignment of `omit' to mandatory field `%s' of type "
                   "`%s'", field_id->get_dispname().c_str(),
                   base_type->get_typename().c_str());
        goto error;
      }
    } else {
      bool is_string_element = subrefs && subrefs->refers_to_string_element();
      self_ref |= type->chk_this_value(val, lhs, Type::EXPECTED_DYNAMIC_VALUE,
        INCOMPLETE_ALLOWED, OMIT_NOT_ALLOWED,
        (is_string_element ? NO_SUB_CHK : SUB_CHK), NOT_IMPLICIT_OMIT,
        (is_string_element ? IS_STR_ELEM : NOT_STR_ELEM));
      if (is_string_element) {
        // The length of RHS value shall be 1.
        Value *v_last = val->get_value_refd_last();
        switch (type->get_type_refd_last()->get_typetype()) {
        case Type::T_BSTR:
        case Type::T_BSTR_A:
          if (v_last->get_valuetype() != Value::V_BSTR) v_last = 0;
          break;
        case Type::T_HSTR:
          if (v_last->get_valuetype() != Value::V_HSTR) v_last = 0;
          break;
        case Type::T_OSTR:
          if (v_last->get_valuetype() != Value::V_OSTR) v_last = 0;
          break;
        case Type::T_CSTR:
        case Type::T_NUMERICSTRING:
        case Type::T_PRINTABLESTRING:
        case Type::T_IA5STRING:
        case Type::T_VISIBLESTRING:
        case Type::T_UTCTIME:
        case Type::T_GENERALIZEDTIME:
          if (v_last->get_valuetype() != Value::V_CSTR) v_last = 0;
          break;
        case Type::T_USTR:
        case Type::T_UTF8STRING:
        case Type::T_TELETEXSTRING:
        case Type::T_VIDEOTEXSTRING:
        case Type::T_GRAPHICSTRING:
        case Type::T_GENERALSTRING:
        case Type::T_UNIVERSALSTRING:
        case Type::T_BMPSTRING:
        case Type::T_OBJECTDESCRIPTOR:
          if (v_last->get_valuetype() != Value::V_USTR) v_last = 0;
          break;
        default:
          v_last = 0;
        }
        if (v_last) {
          size_t string_len = v_last->get_val_strlen();
          if (string_len != 1) {
            val->error("The length of the string to be assigned to a string "
                       "element of type `%s' should be 1 instead of %lu",
                       type->get_typename().c_str(),
                       static_cast<unsigned long>(string_len));
            goto error;
          }
        }
      }
    }
    return;
  error:
    delete ref;
    delete val;
    asstype = ASS_ERROR;
    return;
  }

  void Assignment::chk_template_ass()
  {
    FieldOrArrayRefs *subrefs = ref->get_subrefs();
    Common::Assignment *lhs = ref->get_refd_assignment();
    if (!lhs) FATAL_ERROR("Ttcn::Assignment::chk_template_ass()");
    Type *type = lhs->get_Type()->
      get_field_type(subrefs, Type::EXPECTED_DYNAMIC_VALUE);
    if (!type) goto error;
    if (lhs->get_asstype() != Common::Assignment::A_VAR_TEMPLATE &&
        subrefs && subrefs->refers_to_string_element()) {
      ref->error("It is not allowed to index template strings");
      goto error;
    }
    templ->set_my_governor(type);

    templ->flatten(false);

    type->chk_this_template_ref(templ);
    self_ref |= type->chk_this_template_generic(templ, INCOMPLETE_ALLOWED,
      (type->get_parent_type() != NULL && !type->is_optional_field()) ?
      OMIT_NOT_ALLOWED : OMIT_ALLOWED,
      ANY_OR_OMIT_ALLOWED, SUB_CHK, NOT_IMPLICIT_OMIT, NOT_CLASS_MEMBER_INIT, lhs);
    chk_template_restriction();
    return;
  error:
    delete ref;
    delete templ;
    asstype = ASS_ERROR;
    return;
  }

  void Assignment::chk()
  {
    switch(asstype) {
    case ASS_UNKNOWN:
      chk_unknown_ass();
      break;
    case ASS_VAR:
      chk_var_ass();
      break;
    case ASS_TEMPLATE:
      chk_template_ass();
      break;
    case ASS_ERROR:
      break;
    default:
      FATAL_ERROR("Ttcn::Assignment::chk()");
    } // switch
  }

  void Assignment::set_code_section(
    GovernedSimple::code_section_t p_code_section)
  {
    switch(asstype) {
    case ASS_VAR:
      ref->set_code_section(p_code_section);
      val->set_code_section(p_code_section);
      break;
    case ASS_TEMPLATE:
      ref->set_code_section(p_code_section);
      templ->set_code_section(p_code_section);
      break;
    case ASS_UNKNOWN:
    case ASS_ERROR:
      break;
    default:
      FATAL_ERROR("Ttcn::Assignment::set_code_section()");
    } // switch
  }

  void Assignment::chk_template_restriction()
  {
    if (asstype!=ASS_TEMPLATE)
      FATAL_ERROR("Ttcn::Assignment::chk_template_restriction()");
    Common::Assignment *t_ass = ref->get_refd_assignment();
    if (!t_ass) FATAL_ERROR("Ttcn::Assignment::chk_template_restriction()");
    switch (t_ass->get_asstype()) {
    case Common::Assignment::A_VAR_TEMPLATE: {
      Def_Var_Template* dvt = dynamic_cast<Def_Var_Template*>(t_ass);
      if (!dvt) FATAL_ERROR("Ttcn::Assignment::chk_template_restriction()");
      template_restriction = dvt->get_template_restriction();
    } break;
    case Common::Assignment::A_PAR_TEMPL_IN:
    case Common::Assignment::A_PAR_TEMPL_OUT:
    case Common::Assignment::A_PAR_TEMPL_INOUT: {
      FormalPar* fp = dynamic_cast<FormalPar*>(t_ass);
      if (!fp) FATAL_ERROR("Ttcn::Assignment::chk_template_restriction()");
      template_restriction = fp->get_template_restriction();
    } break;
    default:
      template_restriction = TR_NONE;
    }
    // transform the restriction if this is a subfield
    template_restriction = Template::get_sub_restriction(template_restriction, ref);
    // check the template restriction
    gen_restriction_check =
      templ->chk_restriction("template", template_restriction, this);
  }

  char *Assignment::generate_code(char *str)
  {
    FieldOrArrayRefs *t_subrefs = ref->get_subrefs();
    const bool rhs_copied = self_ref;
    switch (asstype) {
    case ASS_VAR: {
      const string& rhs_copy = val->get_temporary_id();
      string rhs_ref = rhs_copy;
      if (rhs_copied /*&& val->get_valuetype() == Value::V_CHOICE*/) {
        if (val->get_my_governor()->is_optional_field()) {
          str = mputprintf(str, "{\nOPTIONAL<%s> %s;\n",
            val->get_my_governor()->get_genname_value(val->get_my_scope()).c_str(), rhs_copy.c_str());
          rhs_ref += "()";
        } else {
          str = mputprintf(str, "{\n%s %s;\n",
            val->get_my_governor()->get_genname_value(val->get_my_scope()).c_str(), rhs_copy.c_str());
        }
      }
      bool needs_conv = use_runtime_2 && TypeConv::needs_conv_refd(val);
      if (needs_conv) {
        case3:
        // Most complicated case.  The LHS is saved in a temporary reference,
        // in case we need to access it more than once, e.g:
        // x2[1] := { f1 := ..., f2 := ... } in TTCN-3  becomes
        // rectype& tmp = x2[1]; tmp.f1() = ...; tmp.f2() = ...;
        // This saves having to index x2 more than once.
        const string& tmp_id = val->get_temporary_id();  // For "ref".
        const char *tmp_id_str = tmp_id.c_str();
        const string& type_genname =
          val->get_my_governor()->get_genname_value(val->get_my_scope());
        const char *type_genname_str = type_genname.c_str();
        expression_struct expr;
        Code::init_expr(&expr);
        if (in_constructor) {
          ref->set_gen_const_prefix();
        }
        ref->generate_code(&expr);

        if (rhs_copied) {
          if (needs_conv)
            str = TypeConv::gen_conv_code_refd(str, rhs_ref.c_str(), val);
          else   str = val->generate_code_init(str, rhs_ref.c_str());
        }

        str = mputstr(str, "{\n");
        str = mputstr(str, expr.preamble);
        if (t_subrefs && t_subrefs->refers_to_string_element()) {
          // The LHS is a string element.
          str = mputprintf(str, "%s_ELEMENT %s(%s);\n", type_genname_str,
                           tmp_id_str, expr.expr);
        } else {
          // The LHS is a normal value.
          str = mputprintf(str, "%s& %s = %s; /* 7388 */\n", type_genname_str,
            tmp_id_str, expr.expr);
        }
        str = mputstr(str, expr.postamble);
        // We now have a reference to the LHS. Generate the actual assignment
        if (rhs_copied) {
          str = mputprintf(str, "%s = %s;\n", tmp_id_str, rhs_copy.c_str());
        }
        else {
          if (needs_conv)
            str = TypeConv::gen_conv_code_refd(str, tmp_id_str, val);
          else   str = val->generate_code_init(str, tmp_id_str);
        }
        Code::free_expr(&expr);
        str = mputstr(str, "}\n");
      }
      else {
        if (t_subrefs) {
          if (!val->has_single_expr()) goto case3;
          // C++ equivalent of RHS is a single expression.
          expression_struct expr;
          Code::init_expr(&expr);
          if (in_constructor) {
            ref->set_gen_const_prefix();
          }
          ref->generate_code(&expr);// vu.s()
          if (rhs_copied) {
            str = mputprintf(str, "%s = %s;\n",
              rhs_copy.c_str(), val->get_single_expr().c_str());

            expr.expr = mputprintf(expr.expr, " = %s", rhs_copy.c_str());
          }
          else {
            expr.expr = mputprintf(expr.expr,
              " = %s", val->get_single_expr().c_str());
          }
          str = Code::merge_free_expr(str, &expr);
        }
        else {
          // The LHS is a single identifier.
          Common::Assignment* refd_ass = ref->get_refd_assignment();
          string rhs_name = refd_ass->get_genname_from_scope(ref->get_my_scope());
          if (in_constructor &&
              refd_ass->get_asstype() == Common::Assignment::A_CONST) {
            rhs_name = string("const_") + rhs_name;
          }
          else if (ref->get_reftype() == Ref_simple::REF_THIS) {
            rhs_name = string("this->") + rhs_name;
          }
          if (val->can_use_increment(ref)) {
            switch (val->get_optype()) {
            case Value::OPTYPE_ADD:
              str = mputprintf(str, "++%s;\n", rhs_name.c_str());
              break;
            case Value::OPTYPE_SUBTRACT:
              str = mputprintf(str, "--%s;\n", rhs_name.c_str());
              break;
            default:
              FATAL_ERROR("Ttcn::Assignment::generate_code()");
            }
          } else {
            str = val->generate_code_init(str,
              (rhs_copied ? rhs_copy : rhs_name).c_str());

            if (rhs_copied) {
              str = mputprintf(str, "%s = %s;\n", rhs_name.c_str(), rhs_copy.c_str());
            }
          }
        }
      }
      if (rhs_copied) {
        str = mputstr(str, "}\n");
      }
      break; }
    case ASS_TEMPLATE: {
      const string& rhs_copy = templ->get_temporary_id();
      if (rhs_copied /*&& val->get_valuetype() == Value::V_CHOICE*/) {
        str = mputprintf(str, "{\n%s %s;\n",
          templ->get_my_governor()->get_genname_template(templ->get_my_scope()).c_str(), rhs_copy.c_str()//, rhs_copy.c_str()
          );
      }
      bool needs_conv = use_runtime_2 && TypeConv::needs_conv_refd(templ);
      if (needs_conv) { // case 3
        case3t:
        // Most complicated case.  The LHS is saved in a temporary reference.
        const string& tmp_id = templ->get_temporary_id();
        const char *tmp_id_str = tmp_id.c_str();
        expression_struct expr;
        Code::init_expr(&expr);
        if (in_constructor) {
          ref->set_gen_const_prefix();
        }
        ref->generate_code(&expr);

        if (rhs_copied) {
          if (needs_conv)
            str = TypeConv::gen_conv_code_refd(str, rhs_copy.c_str(), templ);
          else str = templ->generate_code_init(str, rhs_copy.c_str());

        }
        str = mputstr(str, "{\n");
        str = mputstr(str, expr.preamble);
        str = mputprintf(str, "%s& %s = %s;\n",
          templ->get_my_governor()->get_genname_template(
            templ->get_my_scope()
            ).c_str(), tmp_id_str, expr.expr);
        str = mputstr(str, expr.postamble);
        if (rhs_copied) {
          str = mputprintf(str, "%s = %s;\n", tmp_id_str, rhs_copy.c_str());
        }
        else {
          if (needs_conv)
            str = TypeConv::gen_conv_code_refd(str, tmp_id_str, templ);
          else {
            if (Common::Type::T_SEQOF == templ->get_my_governor()->get_typetype() ||
                Common::Type::T_ARRAY == templ->get_my_governor()->get_typetype()) {
              str = mputprintf(str, "%s.remove_all_permutations();\n", tmp_id_str);
            }
            str = templ->generate_code_init(str, tmp_id_str);
          }
        }
        Code::free_expr(&expr);

        if (template_restriction != TR_NONE && gen_restriction_check)
          str = Template::generate_restriction_check_code(str,
            tmp_id_str, template_restriction);
        str = mputstr(str, "}\n");
      }
      else { // !needs_conv
        if (t_subrefs) {
          if ((template_restriction == TR_NONE || !gen_restriction_check)
            && templ->has_single_expr()) {
            // C++ equivalent of RHS is a single expression and no restriction
            // check.  Skipped if conversion needed.
            expression_struct expr;
            Code::init_expr(&expr);
            if (in_constructor) {
              ref->set_gen_const_prefix();
            }
            ref->generate_code(&expr);
            if (rhs_copied) {
              str = mputprintf(str, "%s = %s;\n",
                rhs_copy.c_str(), templ->get_single_expr(false).c_str());

              expr.expr = mputprintf(expr.expr, " = %s", rhs_copy.c_str());
            }
            else {
              expr.expr = mputprintf(expr.expr,
                " = %s", templ->get_single_expr(false).c_str());
            }
            str = Code::merge_free_expr(str, &expr); // will add a semicolon
          }
          else goto case3t;
        }
        else {
          // LHS is a single identifier
          Common::Assignment* refd_ass = ref->get_refd_assignment();
          string rhs_name = refd_ass->get_genname_from_scope(ref->get_my_scope());
          if (in_constructor &&
              refd_ass->get_asstype() == Common::Assignment::A_TEMPLATE) {
            rhs_name = string("template_") + rhs_name;
          }
          else if (ref->get_reftype() == Ref_simple::REF_THIS) {
            rhs_name = string("this->") + rhs_name;
          }
          if (Common::Type::T_SEQOF == templ->get_my_governor()->get_typetype() ||
              Common::Type::T_ARRAY == templ->get_my_governor()->get_typetype()) {
            str = mputprintf(str, "%s.remove_all_permutations();\n", (rhs_copied ? rhs_copy : rhs_name).c_str());
          }
          str = templ->generate_code_init(str,
            (rhs_copied ? rhs_copy : rhs_name).c_str());
          if (rhs_copied) {
            str = mputprintf(str, "%s = %s;\n", rhs_name.c_str(), rhs_copy.c_str());
          }
          if (template_restriction != TR_NONE && gen_restriction_check)
            str = Template::generate_restriction_check_code(str,
              ref->get_refd_assignment()->get_genname_from_scope(
                ref->get_my_scope()
                ).c_str(), template_restriction);
        }
      }
      if (rhs_copied) {
        str = mputstr(str, "}\n");
      }
      break; }
    default:
      FATAL_ERROR("Ttcn::Assignment::generate_code()");
    } // switch
    return str;
  }

  // =================================
  // ===== ParamAssignment
  // =================================

  ParamAssignment::ParamAssignment(Identifier *p_id, Reference *p_ref,
                                   bool p_decoded, Value* p_str_enc)
    : Node(), Location(), id(p_id), ref(p_ref), decoded(p_decoded)
    , str_enc(p_str_enc), dec_type(NULL)
  {
    if (!id || !ref || (!decoded && str_enc)) {
      FATAL_ERROR("Ttcn::ParamAssignment::ParamAssignment()");
    }
  }

  ParamAssignment::~ParamAssignment()
  {
    delete id;
    delete ref;
    if (str_enc != NULL) {
      delete str_enc;
    }
  }

  ParamAssignment *ParamAssignment::clone() const
  {
    FATAL_ERROR("ParamAssignment::clone");
  }

  void ParamAssignment::set_my_scope(Scope *p_scope)
  {
    if (!ref) FATAL_ERROR("Ttcn::ParamAssignment::set_my_scope()");
    ref->set_my_scope(p_scope);
    if (str_enc != NULL) {
      str_enc->set_my_scope(p_scope);
    }
  }

  void ParamAssignment::set_fullname(const string& p_fullname)
  {
    if (!ref) FATAL_ERROR("Ttcn::ParamAssignment::set_fullname()");
    ref->set_fullname(p_fullname);
    if (str_enc != NULL) {
      str_enc->set_fullname(p_fullname + ".<string_encoding>");
    }
  }

  Reference *ParamAssignment::get_ref() const
  {
    if (!ref) FATAL_ERROR("Ttcn::ParamAssignment::get_ref()");
    return ref;
  }

  Reference *ParamAssignment::steal_ref()
  {
    if (!ref) FATAL_ERROR("Ttcn::ParamAssignment::steal_ref()");
    Reference *ret_val = ref;
    ref = 0;
    return ret_val;
  }
  
  Value* ParamAssignment::get_str_enc() const
  {
    return str_enc;
  }

  Value* ParamAssignment::steal_str_enc()
  {
    Value *ret_val = str_enc;
    str_enc = NULL;
    return ret_val;
  }

  // =================================
  // ===== ParamAssignments
  // =================================

  ParamAssignments::~ParamAssignments()
  {
    for(size_t i=0; i<parasss.size(); i++)
      delete parasss[i];
    parasss.clear();
  }

  ParamAssignments *ParamAssignments::clone() const
  {
    FATAL_ERROR("ParamAssignments::clone");
  }

  void ParamAssignments::set_my_scope(Scope *p_scope)
  {
    for(size_t i=0; i<parasss.size(); i++)
      parasss[i]->set_my_scope(p_scope);
  }

  void ParamAssignments::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for(size_t i=0; i<parasss.size(); i++)
      parasss[i]->set_fullname(p_fullname+".parass_"+Int2string(i+1));
  }

  void ParamAssignments::add_parass(ParamAssignment *p_parass)
  {
    if(!p_parass)
      FATAL_ERROR("ParamAssignments::add_parass()");
    parasss.add(p_parass);
  }

  // =================================
  // ===== VariableEntry
  // =================================

  VariableEntry::VariableEntry(Reference *p_ref)
    : Node(), Location(), ref(p_ref), decoded(false), str_enc(NULL), dec_type(NULL)
  {
    if(!ref) FATAL_ERROR("VariableEntry::VariableEntry()");
  }
  
  VariableEntry::VariableEntry(Reference* p_ref, bool p_decoded,
                               Value* p_str_enc, Type* p_dec_type)
    : Node(), Location(), ref(p_ref), decoded(p_decoded), str_enc(p_str_enc)
    , dec_type(p_dec_type)
  {
    if(!ref) FATAL_ERROR("VariableEntry::VariableEntry()");
  }

  VariableEntry::~VariableEntry()
  {
    delete ref;
    if (str_enc != NULL) {
      delete str_enc;
    }
  }

  VariableEntry *VariableEntry::clone() const
  {
    FATAL_ERROR("VariableEntry::clone");
  }

  void VariableEntry::set_my_scope(Scope *p_scope)
  {
    if(ref) ref->set_my_scope(p_scope);
    if (str_enc != NULL) {
      str_enc->set_my_scope(p_scope);
    }
  }

  void VariableEntry::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    if(ref) ref->set_fullname(p_fullname+".ref");
    if (str_enc != NULL) {
      str_enc->set_fullname(p_fullname + ".<string_encoding>");
    }
  }

  // =================================
  // ===== VariableEntries
  // =================================

  VariableEntries::~VariableEntries()
  {
    for(size_t i=0; i<ves.size(); i++)
      delete ves[i];
    ves.clear();
  }

  VariableEntries *VariableEntries::clone() const
  {
    FATAL_ERROR("VariableEntries::clone");
  }

  void VariableEntries::set_my_scope(Scope *p_scope)
  {
    for(size_t i=0; i<ves.size(); i++)
      ves[i]->set_my_scope(p_scope);
  }

  void VariableEntries::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for(size_t i=0; i<ves.size(); i++)
      ves[i]->set_fullname(p_fullname+".ve_"+Int2string(i+1));
  }

  void VariableEntries::add_ve(VariableEntry *p_ve)
  {
    if(!p_ve)
      FATAL_ERROR("VariableEntries::add_ve()");
    ves.add(p_ve);
  }

  // =================================
  // ===== ParamRedirect
  // =================================

  ParamRedirect::ParamRedirect(ParamAssignments *p_parasss)
    : Node(), Location(), parredirtype(P_ASS), parasss(p_parasss)
  {
    if(!parasss) FATAL_ERROR("ParamRedirect::ParamRedirect()");
  }

  ParamRedirect::ParamRedirect(VariableEntries *p_ves)
    : Node(), Location(), parredirtype(P_VAR), ves(p_ves)
  {
    if(!ves) FATAL_ERROR("ParamRedirect::ParamRedirect()");
  }

  ParamRedirect::~ParamRedirect()
  {
    switch(parredirtype) {
    case P_ASS:
      delete parasss;
      break;
    case P_VAR:
      delete ves;
      break;
    default:
      FATAL_ERROR("ParamRedirect::~ParamRedirect()");
    } // switch
  }

  ParamRedirect *ParamRedirect::clone() const
  {
    FATAL_ERROR("ParamRedirect::clone");
  }

  void ParamRedirect::set_my_scope(Scope *p_scope)
  {
    switch(parredirtype) {
    case P_ASS:
      parasss->set_my_scope(p_scope);
      break;
    case P_VAR:
      ves->set_my_scope(p_scope);
      break;
    default:
      FATAL_ERROR("ParamRedirect::set_my_scope()");
    } // switch
  }

  void ParamRedirect::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    switch(parredirtype) {
    case P_ASS:
      parasss->set_fullname(p_fullname+".parasss");
      break;
    case P_VAR:
      ves->set_fullname(p_fullname+".parvars");
      break;
    default:
      FATAL_ERROR("ParamRedirect::set_fullname()");
    } // switch
  }

  void ParamRedirect::chk_erroneous()
  {
    Error_Context cntxt(this, "In parameter redirect");
    switch(parredirtype) {
    case P_ASS: {
      map<string, ParamAssignment> parass_m;
      for (size_t i = 0; i < parasss->get_nof_parasss(); i++) {
	ParamAssignment *t_parass = parasss->get_parass_byIndex(i);
	const Identifier &t_id = t_parass->get_id();
	const string& name = t_id.get_name();
	const char *dispname_str = t_id.get_dispname().c_str();
	if (parass_m.has_key(name)) {
	  t_parass->error("Duplicate redirect for parameter `%s'",
	    dispname_str);
	  parass_m[name]->note("A variable entry for parameter `%s' is "
	    "already given here", dispname_str);
	} else parass_m.add(name, t_parass);
	Error_Context cntxt2(t_parass, "In redirect for parameter `%s'",
	  dispname_str);
	chk_variable_ref(t_parass->get_ref(), 0);
  Value* str_enc = t_parass->get_str_enc();
  if (str_enc != NULL) {
    str_enc->chk_string_encoding(NULL);
  }
      }
      parass_m.clear();
      break; }
    case P_VAR:
      for (size_t i = 0; i < ves->get_nof_ves(); i++) {
        VariableEntry *t_ve = ves->get_ve_byIndex(i);
	Error_Context cntxt2(t_ve, "In variable entry #%lu",
         static_cast<unsigned long>(i + 1));
	chk_variable_ref(t_ve->get_ref(), 0);
      }
      break;
    default:
      FATAL_ERROR("ParamRedirect::chk_erroneous()");
    } // switch
  }

  void ParamRedirect::chk(Type *p_sig, bool is_out)
  {
    SignatureParamList *t_parlist = p_sig->get_signature_parameters();
    if (t_parlist) {
      Error_Context cntxt(this, "In parameter redirect");
      switch (parredirtype) {
      case P_ASS:
	chk_parasss(p_sig, t_parlist, is_out);
	break;
      case P_VAR:
	chk_ves(p_sig, t_parlist, is_out);
	break;
      default:
	FATAL_ERROR("ParamRedirect::chk()");
      }
    } else {
      error("Parameter redirect cannot be used because signature `%s' "
	"does not have parameters", p_sig->get_typename().c_str());
      chk_erroneous();
    }
  }

  void ParamRedirect::chk_parasss(Type *p_sig, SignatureParamList *p_parlist,
    bool is_out)
  {
    map<string, ParamAssignment> parass_m;
    bool error_flag = false;
    for (size_t i = 0; i < parasss->get_nof_parasss(); i++) {
      ParamAssignment *t_parass = parasss->get_parass_byIndex(i);
      const Identifier &t_id = t_parass->get_id();
      const string& name = t_id.get_name();
      const char *dispname_str = t_id.get_dispname().c_str();
      if (parass_m.has_key(name)) {
	t_parass->error("Duplicate redirect for parameter `%s'",
	  dispname_str);
	parass_m[name]->note("A variable entry for parameter `%s' is "
	  "already given here", dispname_str);
	error_flag = true;
      } else parass_m.add(name, t_parass);
      Error_Context cntxt2(t_parass, "In redirect for parameter `%s'",
	dispname_str);
      if (p_parlist->has_param_withName(t_id)) {
	const SignatureParam *t_par = p_parlist->get_param_byName(t_id);
	SignatureParam::param_direction_t t_dir = t_par->get_direction();
	if (is_out) {
	  if (t_dir == SignatureParam::PARAM_IN) {
	    t_parass->error("Parameter `%s' of signature `%s' has `in' "
	      "direction", dispname_str, p_sig->get_typename().c_str());
	    error_flag = true;
	  }
	} else {
	  if (t_dir == SignatureParam::PARAM_OUT) {
	    t_parass->error("Parameter `%s' of signature `%s' has `out' "
	      "direction", dispname_str, p_sig->get_typename().c_str());
	    error_flag = true;
	  }
	}
  if (t_parass->is_decoded()) {
    Value* str_enc = t_parass->get_str_enc();
    switch (t_par->get_type()->get_type_refd_last()->get_typetype_ttcn3()) {
    case Type::T_BSTR:
    case Type::T_HSTR:
    case Type::T_OSTR:
    case Type::T_CSTR:
      if (str_enc != NULL) {
        str_enc->error("The encoding formal parameter for the '@decoded' modifier "
          "is only available to parameter redirects of universal charstrings");
        error_flag = true;
      }
      break;
    case Type::T_USTR:
      if (str_enc != NULL) {
        str_enc->chk_string_encoding(NULL);
      }
      break;
    default:
      t_parass->error("The '@decoded' modifier is only available to parameter "
        "redirects of string types.");
      error_flag = true;
      break;
    }
    // the redirected parameter could be decoded into any type
    Type *t_var_type = t_parass->get_ref()->chk_variable_ref();
    if (!error_flag && t_var_type != NULL) {
      // make sure the variable's type has a decoding type set, and store it
      t_parass->set_dec_type(t_var_type->get_type_refd());
      t_parass->get_dec_type()->chk_coding(false,
        t_parass->get_ref()->get_my_scope()->get_scope_mod());
    }
  }
  else {
    chk_variable_ref(t_parass->get_ref(), t_par->get_type());
  }
      } else {
	t_parass->error("Signature `%s' does not have parameter named `%s'",
	  p_sig->get_typename().c_str(), dispname_str);
	error_flag = true;
	chk_variable_ref(t_parass->get_ref(), 0);
  Value* str_enc = t_parass->get_str_enc();
  if (str_enc != NULL) {
    str_enc->chk_string_encoding(NULL);
  }
      }
    }
    if (!error_flag) {
      // converting the AssignmentList to VariableList
      VariableEntries *t_ves = new VariableEntries;
      size_t upper_limit = is_out ?
        p_parlist->get_nof_out_params() : p_parlist->get_nof_in_params();
      for (size_t i = 0; i < upper_limit; i++) {
	SignatureParam *t_par = is_out ? p_parlist->get_out_param_byIndex(i)
	  : p_parlist->get_in_param_byIndex(i);
	const string& name = t_par->get_id().get_name();
	if (parass_m.has_key(name))
	  t_ves->add_ve(new VariableEntry(parass_m[name]->steal_ref(), 
      parass_m[name]->is_decoded(), parass_m[name]->steal_str_enc(),
      parass_m[name]->get_dec_type()));
	else t_ves->add_ve(new VariableEntry);
      }
      delete parasss;
      ves = t_ves;
      parredirtype = P_VAR;
    }
    parass_m.clear();
  }

  void ParamRedirect::chk_ves(Type *p_sig, SignatureParamList *p_parlist,
    bool is_out)
  {
    size_t nof_ves = ves->get_nof_ves();
    size_t nof_pars = is_out ?
      p_parlist->get_nof_out_params() : p_parlist->get_nof_in_params();
    if (nof_ves != nof_pars) {
      error("Too %s variable entries compared to the number of %s/inout "
	"parameters in signature `%s': %lu was expected instead of %lu",
	nof_ves > nof_pars ? "many" : "few", is_out ? "out" : "in",
	p_sig->get_typename().c_str(), static_cast<unsigned long>( nof_pars ),
        static_cast<unsigned long>( nof_ves ));
    }
    for (size_t i = 0; i < nof_ves; i++) {
      VariableEntry *t_ve = ves->get_ve_byIndex(i);
      if (i < nof_pars) {
	SignatureParam *t_par = is_out ? p_parlist->get_out_param_byIndex(i)
	  : p_parlist->get_in_param_byIndex(i);
	Error_Context cntxt(t_ve, "In variable entry #%lu (for parameter `%s')",
	  static_cast<unsigned long>(i + 1), t_par->get_id().get_dispname().c_str());
        chk_variable_ref(t_ve->get_ref(), t_par->get_type());
      } else {
	Error_Context cntxt(t_ve, "In variable entry #%lu",
          static_cast<unsigned long>(i + 1));
        chk_variable_ref(t_ve->get_ref(), 0);
      }
    }
  }

  void ParamRedirect::chk_variable_ref(Reference *p_ref, Type *p_type)
  {
    if (!p_ref) return;
    Type *t_var_type = p_ref->chk_variable_ref();
    if (p_type && t_var_type && !p_type->is_identical(t_var_type)) {
      p_ref->error("Type mismatch in parameter redirect: "
	"A variable of type `%s' was expected instead of `%s'",
	p_type->get_typename().c_str(),
	t_var_type->get_typename().c_str());
    }
  }

  void ParamRedirect::set_code_section(
    GovernedSimple::code_section_t p_code_section)
  {
    // code can be generated from VariableList only
    switch (parredirtype) {
    case P_ASS:
      break;
    case P_VAR:
      for (size_t i = 0; i < ves->get_nof_ves(); i++) {
	Reference *t_ref = ves->get_ve_byIndex(i)->get_ref();
	if (t_ref) t_ref->set_code_section(p_code_section);
      }
      break;
    default:
      FATAL_ERROR("ParamRedirect::set_code_section()");
    }
  }

  void ParamRedirect::generate_code(expression_struct_t *expr,
                                    TemplateInstance* matched_ti, bool /*is_out*/)
  {
    // AssignmentList is converted to VariableList during checking
    if (parredirtype != P_VAR) FATAL_ERROR("ParamRedirect::generate_code()");
    if (has_decoded_modifier()) {
      expr->expr = mputprintf(expr->expr, "&(%s), ", matched_ti->get_last_gen_expr());
    }
    for (size_t i = 0; i < ves->get_nof_ves(); i++) {
      if (i > 0) expr->expr = mputstr(expr->expr, ", ");
      VariableEntry* ve = ves->get_ve_byIndex(i);
      // there's an extra charstring parameter for every decoded universal
      // charstring parameter redirect with an unfoldable encoding format
      Value* str_enc = (ve->is_decoded() && ve->get_str_enc() != NULL &&
        ve->get_str_enc()->is_unfoldable()) ? ve->get_str_enc() : NULL;
      Reference *ref = ve->get_ref();
      if (ref) {
        // the variable reference is present
        expr->expr = mputstr(expr->expr, "&(");
        ref->generate_code(expr);
        expr->expr = mputc(expr->expr, ')');
        if (str_enc != NULL) {
          expr->expr = mputstr(expr->expr, ", ");
          str_enc->generate_code_expr(expr);
        }
      }
      else {
        expr->expr = mputstr(expr->expr, "NULL");
        if (str_enc != NULL) {
          expr->expr = mputstr(expr->expr, ", CHARSTRING()");
        }
      }
    }
  }
  
  char* ParamRedirect::generate_code_decoded(char* str, TemplateInstance* matched_ti,
                                             const char* tmp_id, bool is_out)
  {
    // AssignmentList is converted to VariableList during checking
    if (parredirtype != P_VAR) {
      FATAL_ERROR("ParamRedirect::generate_code_decoded()");
    }
    Scope* scope = NULL;
    for (size_t i = 0; i < ves->get_nof_ves(); i++) {
      Reference* ref = ves->get_ve_byIndex(i)->get_ref();
      if (ref != NULL) {
        // there must be at least one reference in the redirect, otherwise this
        // function would not be called
        scope = ref->get_my_scope();
        break;
      }
    }
    // cycle through the variable list and gather all the data needed for the
    // new class
    char* members_str = NULL;
    char* constr_params_str = NULL;
    char* base_constr_params_str = NULL;
    char* constr_init_list_str = NULL;
    char* set_params_str = NULL;
    Type* sig_type = matched_ti->get_Template()->get_my_governor()->get_type_refd_last();
    Type* return_type = sig_type->get_signature_return_type();
    if (return_type != NULL && is_out) {
      // the return type's value redirect object must be passed through this
      // class
      if (use_runtime_2) {
        constr_params_str = mprintf("Value_Redirect_Interface* return_redirect, ");
      }
      else {
        constr_params_str = mprintf("%s* return_redirect, ",
          return_type->get_genname_value(scope).c_str());
      }
      base_constr_params_str = mcopystr("return_redirect");
    }
    // store a pointer to the matched template, the decoding results from
    // decmatch templates might be reused to optimize decoded parameter redirects
    members_str = mprintf("%s* ptr_matched_temp;\n",
      sig_type->get_genname_template(scope).c_str());
    constr_params_str = mputprintf(constr_params_str, "%s* par_matched_temp",
      sig_type->get_genname_template(scope).c_str());
    constr_init_list_str = mcopystr(", ptr_matched_temp(par_matched_temp)");
    SignatureParamList* parlist = sig_type->get_signature_parameters();
    for (size_t i = 0; i < ves->get_nof_ves(); i++) {
      VariableEntry* ve = ves->get_ve_byIndex(i);
      SignatureParam* par = is_out ? parlist->get_out_param_byIndex(i) :
        parlist->get_in_param_byIndex(i);
      const char* par_name = par->get_id().get_name().c_str();
      if (constr_params_str != NULL) {
        constr_params_str = mputstr(constr_params_str, ", ");
      }
      if (base_constr_params_str != NULL) {
        base_constr_params_str = mputstr(base_constr_params_str, ", ");
      }
      if (ve->is_decoded()) {
        members_str = mputprintf(members_str, "%s* ptr_%s_dec;\n",
          ve->get_dec_type()->get_genname_value(scope).c_str(), par_name);
        constr_params_str = mputprintf(constr_params_str, "%s* par_%s_dec = NULL",
          ve->get_dec_type()->get_genname_value(scope).c_str(), par_name);
        base_constr_params_str = mputstr(base_constr_params_str, "NULL");
        constr_init_list_str = mputprintf(constr_init_list_str,
          ", ptr_%s_dec(par_%s_dec)", par_name, par_name);
        set_params_str = mputprintf(set_params_str,
          "if (ptr_%s_dec != NULL) {\n", par_name);
        NamedTemplate* matched_named_temp = 
          matched_ti->get_Template()->get_templatetype() == Template::NAMED_TEMPLATE_LIST ?
          matched_ti->get_Template()->get_namedtemp_byName(par->get_id()) : NULL;
        Template* matched_temp = matched_named_temp != NULL ?
          matched_named_temp->get_template()->get_template_refd_last() : NULL;
        bool use_decmatch_result = matched_temp != NULL &&
          matched_temp->get_templatetype() == Template::DECODE_MATCH;
        bool needs_decode = true;
        expression_struct redir_coding_expr;
        Code::init_expr(&redir_coding_expr);
        if (par->get_type()->get_type_refd_last()->get_typetype_ttcn3() == Type::T_USTR) {
          if (ve->get_str_enc() == NULL || !ve->get_str_enc()->is_unfoldable()) {
            const char* redir_coding_str;
            if (ve->get_str_enc() == NULL ||
                ve->get_str_enc()->get_val_str() == "UTF-8") {
              redir_coding_str = "UTF_8";
            }
            else if (ve->get_str_enc()->get_val_str() == "UTF-16" ||
                     ve->get_str_enc()->get_val_str() == "UTF-16BE") {
              redir_coding_str = "UTF16BE";
            }
            else if (ve->get_str_enc()->get_val_str() == "UTF-16LE") {
              redir_coding_str = "UTF16LE";
            }
            else if (ve->get_str_enc()->get_val_str() == "UTF-32LE") {
              redir_coding_str = "UTF32LE";
            }
            else {
              redir_coding_str = "UTF32BE";
            }
            redir_coding_expr.expr = mprintf("CharCoding::%s", redir_coding_str);
          }
          else {
            redir_coding_expr.preamble = mprintf(
              "CharCoding::CharCodingType coding = UNIVERSAL_CHARSTRING::"
              "get_character_coding(enc_fmt_%s, \"decoded parameter redirect\");\n",
              par_name);
            redir_coding_expr.expr = mcopystr("coding");
          }
        }
        if (use_decmatch_result) {
          // if the redirected parameter was matched using a decmatch template,
          // then the parameter redirect class should use the decoding result 
          // from the template instead of decoding the parameter again
          needs_decode = false;
          Type* decmatch_type = matched_temp->get_decode_target()->get_expr_governor(
            Type::EXPECTED_TEMPLATE)->get_type_refd_last();
          if (ve->get_dec_type() != decmatch_type) {
            // the decmatch template and this parameter redirect decode two
            // different types, so just decode the parameter
            needs_decode = true;
            use_decmatch_result = false;
          }
          else if (par->get_type()->get_type_refd_last()->get_typetype_ttcn3() ==
                   Type::T_USTR) {
            // for universal charstrings the situation could be trickier
            // compare the string encodings
            bool different_ustr_encodings = false;
            bool unknown_ustr_encodings = false;
            if (ve->get_str_enc() == NULL) {
              if (matched_temp->get_string_encoding() != NULL) {
                if (matched_temp->get_string_encoding()->is_unfoldable()) {
                  unknown_ustr_encodings = true;
                }
                else if (matched_temp->get_string_encoding()->get_val_str() != "UTF-8") {
                  different_ustr_encodings = true;
                }
              }
            }
            else if (ve->get_str_enc()->is_unfoldable()) {
              unknown_ustr_encodings = true;
            }
            else if (matched_temp->get_string_encoding() == NULL) {
              if (ve->get_str_enc()->get_val_str() != "UTF-8") {
                different_ustr_encodings = true;
              }
            }
            else if (matched_temp->get_string_encoding()->is_unfoldable()) {
              unknown_ustr_encodings = true;
            }
            else if (ve->get_str_enc()->get_val_str() !=
                     matched_temp->get_string_encoding()->get_val_str()) {
              different_ustr_encodings = true;
            }
            if (unknown_ustr_encodings) {
              // the decision of whether to use the decmatch result or to decode
              // the value is made at runtime
              needs_decode = true;
              set_params_str = mputprintf(set_params_str,
                "%sif (%s == ptr_matched_temp->%s().get_decmatch_str_enc()) {\n",
                redir_coding_expr.preamble != NULL ? redir_coding_expr.preamble : "",
                redir_coding_expr.expr, par_name);
            }
            else if (different_ustr_encodings) {
              // if the encodings are different, then ignore the decmatch result
              // and just generate the decoding code as usual
              needs_decode = true;
              use_decmatch_result = false;
            }
          }
        }
        else {
          // it might still be a decmatch template if it's not known at compile-time
          bool unfoldable = matched_temp == NULL;
          if (!unfoldable) {
            switch (matched_temp->get_templatetype()) {
            case Template::ANY_VALUE:
            case Template::ANY_OR_OMIT:
            case Template::BSTR_PATTERN:
            case Template::CSTR_PATTERN:
            case Template::HSTR_PATTERN:
            case Template::OSTR_PATTERN:
            case Template::USTR_PATTERN:
            case Template::COMPLEMENTED_LIST:
            case Template::VALUE_LIST:
            case Template::CONJUNCTION_MATCH:
            case Template::VALUE_RANGE:
            case Template::OMIT_VALUE:
              // it's known at compile-time, and not a decmatch template
              break;
            default:
              // needs runtime check
              unfoldable = true;
              break;
            }
          }
          if (unfoldable) {
            // the decmatch-check must be done at runtime
            use_decmatch_result = true;
            if (redir_coding_expr.preamble != NULL) {
              set_params_str = mputstr(set_params_str, redir_coding_expr.preamble);
            }
            set_params_str = mputprintf(set_params_str,
              "if (ptr_matched_temp->%s().get_selection() == DECODE_MATCH && "
              // the type the parameter was decoded to in the template must be the same
              // as the type this parameter redirect would decode the redirected parameter to
              // (this is checked by comparing the addresses of the type descriptors)
              "&%s_descr_ == ptr_matched_temp->%s().get_decmatch_type_descr()",
              par_name, ve->get_dec_type()->get_genname_typedescriptor(scope).c_str(),
              par_name);
            if (redir_coding_expr.expr != NULL) {
              set_params_str = mputprintf(set_params_str,
                " && %s == ptr_matched_temp->%s().get_decmatch_str_enc()",
                redir_coding_expr.expr, par_name);
            }
            set_params_str = mputstr(set_params_str, ") {\n");
          }
        }
        Code::free_expr(&redir_coding_expr);
        if (use_decmatch_result) {
          set_params_str = mputprintf(set_params_str,
            "*ptr_%s_dec = *((%s*)ptr_matched_temp->%s().get_decmatch_dec_res());\n",
            par_name, ve->get_dec_type()->get_genname_value(scope).c_str(), par_name);
        }
        if (needs_decode) {
          if (use_decmatch_result) {
            set_params_str = mputstr(set_params_str, "}\nelse {\n");
          }
          Type::typetype_t tt = par->get_type()->get_type_refd_last()->get_typetype_ttcn3();
          if (legacy_codec_handling &&
              ve->get_dec_type()->is_coding_by_function(false)) {
            set_params_str = mputstr(set_params_str, "BITSTRING buff(");
            switch (tt) {
            case Type::T_BSTR:
              set_params_str = mputprintf(set_params_str, "par.%s()", par_name);
              break;
            case Type::T_HSTR:
              set_params_str = mputprintf(set_params_str, "hex2bit(par.%s())",
                par_name);
              break;
            case Type::T_OSTR:
              set_params_str = mputprintf(set_params_str, "oct2bit(par.%s())",
                par_name);
              break;
            case Type::T_CSTR:
              set_params_str = mputprintf(set_params_str,
                "oct2bit(char2oct(par.%s()))", par_name);
              break;
            case Type::T_USTR:
              set_params_str = mputprintf(set_params_str,
                "oct2bit(unichar2oct(par.%s(), ", par_name);
              if (ve->get_str_enc() == NULL || !ve->get_str_enc()->is_unfoldable()) {
                // encoding format is missing or is known at compile-time
                set_params_str = mputprintf(set_params_str, "\"%s\"",
                  ve->get_str_enc() != NULL ?
                  ve->get_str_enc()->get_val_str().c_str() : "UTF-8");
              }
              else {
                // the encoding format is not known at compile-time, so an extra
                // member and constructor parameter is needed to store it
                members_str = mputprintf(members_str, "CHARSTRING enc_fmt_%s;\n",
                  par_name);
                constr_params_str = mputprintf(constr_params_str,
                  ", CHARSTRING par_fmt_%s = CHARSTRING()", par_name);
                constr_init_list_str = mputprintf(constr_init_list_str,
                  ", enc_fmt_%s(par_fmt_%s)", par_name, par_name);
                set_params_str = mputprintf(set_params_str, "enc_fmt_%s", par_name);
              }
              set_params_str = mputstr(set_params_str, "))");
              break;
            default:
              FATAL_ERROR("ParamRedirect::generate_code_decoded");
            }
            set_params_str = mputprintf(set_params_str,
              ");\n"
              "if (%s(buff, *ptr_%s_dec) != 0) {\n"
              "TTCN_error(\"Decoding failed in parameter redirect "
              "(for parameter '%s').\");\n"
              "}\n"
              "if (buff.lengthof() != 0) {\n"
              "TTCN_error(\"Parameter redirect (for parameter '%s') failed, "
              "because the buffer was not empty after decoding. "
              "Remaining bits: %%d.\", buff.lengthof());\n"
              "}\n", ve->get_dec_type()->get_legacy_coding_function(false)->
              get_genname_from_scope(scope).c_str(), par_name, par_name, par_name);
          }
          else if (legacy_codec_handling) { // legacy codec handling with built-in decoding
            switch (tt) {
            case Type::T_OSTR:
            case Type::T_CSTR:
              set_params_str = mputprintf(set_params_str,
                "TTCN_Buffer buff(par.%s());\n", par_name);
              break;
            case Type::T_BSTR:
              set_params_str = mputprintf(set_params_str,
                "OCTETSTRING os(bit2oct(par.%s()));\n"
                "TTCN_Buffer buff(os);\n", par_name);
              break;
            case Type::T_HSTR:
              set_params_str = mputprintf(set_params_str,
                "OCTETSTRING os(hex2oct(par.%s()));\n"
                "TTCN_Buffer buff(os);\n", par_name);
              break;
            case Type::T_USTR:
              set_params_str = mputstr(set_params_str, "TTCN_Buffer buff;\n");
              if (ve->get_str_enc() == NULL || !ve->get_str_enc()->is_unfoldable()) {
                // if the encoding format is missing or is known at compile-time, then
                // use the appropriate string encoding function
                string str_enc = (ve->get_str_enc() != NULL) ?
                  ve->get_str_enc()->get_val_str() : string("UTF-8");
                if (str_enc == "UTF-8") {
                  set_params_str = mputprintf(set_params_str,
                    "par.%s().encode_utf8(buff, false);\n", par_name);
                }
                else if (str_enc == "UTF-16" || str_enc == "UTF-16LE" ||
                         str_enc == "UTF-16BE") {
                  set_params_str = mputprintf(set_params_str,
                    "par.%s().encode_utf16(buff, CharCoding::UTF16%s);\n", par_name,
                    (str_enc == "UTF-16LE") ? "LE" : "BE");
                }
                else if (str_enc == "UTF-32" || str_enc == "UTF-32LE" ||
                         str_enc == "UTF-32BE") {
                  set_params_str = mputprintf(set_params_str,
                    "par.%s().encode_utf32(buff, CharCoding::UTF32%s);\n", par_name,
                    (str_enc == "UTF-32LE") ? "LE" : "BE");
                }
              }
              else {
                // the encoding format is not known at compile-time, so an extra
                // member and constructor parameter is needed to store it
                members_str = mputprintf(members_str, "CHARSTRING enc_fmt_%s;\n",
                  par_name);
                constr_params_str = mputprintf(constr_params_str,
                  ", CHARSTRING par_fmt_%s = CHARSTRING()", par_name);
                constr_init_list_str = mputprintf(constr_init_list_str,
                  ", enc_fmt_%s(par_fmt_%s)", par_name, par_name);
                if (!use_decmatch_result) {
                  // if the decmatch result code is generated too, then this variable
                  // was already generated before the main 'if'
                  set_params_str = mputprintf(set_params_str,
                    "CharCoding::CharCodingType coding = UNIVERSAL_CHARSTRING::"
                    "get_character_coding(enc_fmt_%s, \"decoded parameter redirect\");\n",
                    par_name);
                }
                set_params_str = mputprintf(set_params_str,
                  "switch (coding) {\n"
                  "case CharCoding::UTF_8:\n"
                  "par.%s().encode_utf8(buff, false);\n"
                  "break;\n"
                  "case CharCoding::UTF16:\n"
                  "case CharCoding::UTF16LE:\n"
                  "case CharCoding::UTF16BE:\n"
                  "par.%s().encode_utf16(buff, coding);\n"
                  "break;\n"
                  "case CharCoding::UTF32:\n"
                  "case CharCoding::UTF32LE:\n"
                  "case CharCoding::UTF32BE:\n"
                  "par.%s().encode_utf32(buff, coding);\n"
                  "break;\n"
                  "default:\n"
                  "break;\n"
                  "}\n", par_name, par_name, par_name);
              }
              break;
            default:
              FATAL_ERROR("ParamRedirect::generate_code_decoded");
            }
            set_params_str = mputprintf(set_params_str,
              "ptr_%s_dec->decode(%s_descr_, buff, TTCN_EncDec::CT_%s);\n"
              "if (buff.get_read_len() != 0) {\n"
              "TTCN_error(\"Parameter redirect (for parameter '%s') failed, "
              "because the buffer was not empty after decoding. "
              "Remaining octets: %%d.\", static_cast<int>( buff.get_read_len() ));\n"
              "}\n", par_name,
              ve->get_dec_type()->get_genname_typedescriptor(scope).c_str(),
              ve->get_dec_type()->get_coding(false).c_str(), par_name);
          }
          else { // new codec handling
            set_params_str = mputstr(set_params_str, "OCTETSTRING buff(");
            switch (tt) {
            case Type::T_BSTR:
              set_params_str = mputprintf(set_params_str, "bit2oct(par.%s())",
                par_name);
              break;
            case Type::T_HSTR:
              set_params_str = mputprintf(set_params_str, "hex2oct(par.%s())",
                par_name);
              break;
            case Type::T_OSTR:
              set_params_str = mputprintf(set_params_str, "par.%s()", par_name);
              break;
            case Type::T_CSTR:
              set_params_str = mputprintf(set_params_str, "char2oct(par.%s())",
                par_name);
              break;
            case Type::T_USTR:
              set_params_str = mputprintf(set_params_str,
                "unichar2oct(par.%s(), ", par_name);
              if (ve->get_str_enc() == NULL || !ve->get_str_enc()->is_unfoldable()) {
                // encoding format is missing or is known at compile-time
                set_params_str = mputprintf(set_params_str, "\"%s\"",
                  ve->get_str_enc() != NULL ?
                  ve->get_str_enc()->get_val_str().c_str() : "UTF-8");
              }
              else {
                // the encoding format is not known at compile-time, so an extra
                // member and constructor parameter is needed to store it
                members_str = mputprintf(members_str, "CHARSTRING enc_fmt_%s;\n",
                  par_name);
                constr_params_str = mputprintf(constr_params_str,
                  ", CHARSTRING par_fmt_%s = CHARSTRING()", par_name);
                constr_init_list_str = mputprintf(constr_init_list_str,
                  ", enc_fmt_%s(par_fmt_%s)", par_name, par_name);
                set_params_str = mputprintf(set_params_str, "enc_fmt_%s", par_name);
              }
              set_params_str = mputstr(set_params_str, ")");
              break;
            default:
              FATAL_ERROR("ParamRedirect::generate_code_decoded");
            }
            set_params_str = mputprintf(set_params_str,
              ");\n"
              "if (%s_decoder(buff, *ptr_%s_dec, %s_default_coding) != 0) {\n"
              "TTCN_error(\"Decoding failed in parameter redirect "
              "(for parameter '%s').\");\n"
              "}\n"
              "if (buff.lengthof() != 0) {\n"
              "TTCN_error(\"Parameter redirect (for parameter '%s') failed, "
              "because the buffer was not empty after decoding. "
              "Remaining octets: %%d.\", buff.lengthof());\n"
              "}\n",
              ve->get_dec_type()->get_genname_coder(scope).c_str(),
              par_name,
              ve->get_dec_type()->get_genname_default_coding(scope).c_str(),
              par_name, par_name);
          }
          if (use_decmatch_result) {
            set_params_str = mputstr(set_params_str, "}\n");
          }
        }
        set_params_str = mputstr(set_params_str, "}\n");
      }
      else {
        constr_params_str = mputprintf(constr_params_str, "%s* par_%s = NULL",
          par->get_type()->get_genname_value(scope).c_str(), par_name);
        base_constr_params_str = mputprintf(base_constr_params_str, "par_%s",
          par_name);
      }
    }
    // generate the new class with the gathered data
    string qualified_sig_name_ = sig_type->get_genname_value(scope);
    const char* qualified_sig_name = qualified_sig_name_.c_str();
    string unqualified_sig_name_ = sig_type->get_genname_value(
      sig_type->get_my_scope());
    const char* unqualified_sig_name = unqualified_sig_name_.c_str();
    const char* op_name = is_out ? "reply" : "call";
    str = mputprintf(str,
      "class %s_%s_redirect_%s : public %s_%s_redirect {\n"
      // member declarations for the decoded parameters and their encoding formats
      // (if they have one)
      "%s" 
      "public:\n"
      // constructor:
      // same as the inherited constructor, except the types of the parameter
      // redirects with the '@decoded' modifier are replaced with the decoded
      // types (plus an extra encoding format parameter is added when necessary)
      "%s_%s_redirect_%s(%s)\n"
      // the base constructor is called with the parameters that don't need to
      // be decoded; the ones that need to be decoded are set to NULL and are
      // initialized in the member initializer list (together with their
      // eventual encoding formats)
      ": %s_%s_redirect(%s)%s { }\n"
      // set_parameters function: decodes the parameters that need decoding,
      // the ones that don't are sent to the base class' function
      "virtual void set_parameters(const %s_%s& par) const\n"
      "{\n"
      "%s" 
      "%s_%s_redirect::set_parameters(par);\n"
      "}\n"
      "};\n", unqualified_sig_name, op_name, tmp_id, qualified_sig_name, op_name,
      members_str, unqualified_sig_name, op_name, tmp_id, constr_params_str,
      unqualified_sig_name, op_name, base_constr_params_str, constr_init_list_str,
      qualified_sig_name, op_name, set_params_str, unqualified_sig_name, op_name);
    Free(members_str);
    Free(constr_params_str);
    Free(base_constr_params_str);
    Free(constr_init_list_str);
    Free(set_params_str);
    return str;
  }
  
  bool ParamRedirect::has_decoded_modifier() const
  {
    // this is called during code generation
    // AssignmentList is converted to VariableList during checking
    if (parredirtype != P_VAR) {
      FATAL_ERROR("ParamRedirect::generate_code()");
    }
    for (size_t i = 0; i < ves->get_nof_ves(); i++) {
      if (ves->get_ve_byIndex(i)->is_decoded()) {
        return true;
      }
    }
    return false;
  }
  
  // =================================
  // ===== SingleValueRedirect
  // =================================
  
  SingleValueRedirect::SingleValueRedirect(Reference* p_var_ref)
  : Node(), Location(), var_ref(p_var_ref), subrefs(NULL), decoded(false)
  , str_enc(NULL), dec_type(NULL)
  {
    if (var_ref == NULL) {
      FATAL_ERROR("SingleValueRedirect::SingleValueRedirect");
    }
  }
  
  SingleValueRedirect::SingleValueRedirect(Reference* p_var_ref,
                                           FieldOrArrayRefs* p_subrefs,
                                           bool p_decoded, Value* p_str_enc)
  : Node(), Location(), var_ref(p_var_ref), subrefs(p_subrefs)
  , decoded(p_decoded), str_enc(p_str_enc), dec_type(NULL)
  {
    if (var_ref == NULL || subrefs == NULL || (!decoded && str_enc != NULL)) {
      FATAL_ERROR("SingleValueRedirect::SingleValueRedirect");
    }
  }
  
  SingleValueRedirect::~SingleValueRedirect()
  {
    delete var_ref;
    if (subrefs != NULL) {
      delete subrefs;
    }
    if (str_enc != NULL) {
      delete str_enc;
    }
  }
  
  SingleValueRedirect* SingleValueRedirect::clone() const
  {
    FATAL_ERROR("SingleValueRedirect::clone");
  }
  
  void SingleValueRedirect::set_my_scope(Scope* p_scope)
  {
    var_ref->set_my_scope(p_scope);
    if (subrefs != NULL) {
      subrefs->set_my_scope(p_scope);
    }
    if (str_enc != NULL) {
      str_enc->set_my_scope(p_scope);
    }
  }

  void SingleValueRedirect::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    var_ref->set_fullname(p_fullname + ".varref");
    if (subrefs != NULL) {
      subrefs->set_fullname(p_fullname + ".fieldrefs");
    }
    if (str_enc != NULL) {
      str_enc->set_fullname(p_fullname + ".<string_encoding>");
    }
  }
  
  // =================================
  // ==== ValueRedirect
  // =================================
  
  ValueRedirect::~ValueRedirect()
  {
    for (size_t i = 0; i < v.size(); ++i) {
      delete v[i];
    }
    v.clear();
  }
  
  ValueRedirect* ValueRedirect::clone() const
  {
    FATAL_ERROR("ValueRedirect::clone");
  }
  
  void ValueRedirect::set_my_scope(Scope* p_scope)
  {
    for (size_t i = 0; i < v.size(); ++i) {
      v[i]->set_my_scope(p_scope);
    }
  }
  
  void ValueRedirect::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for (size_t i = 0; i < v.size(); ++i) {
      v[i]->set_fullname(p_fullname + ".redirect_" + Int2string(i + 1));
    }
  }
  
  void ValueRedirect::set_code_section(GovernedSimple::code_section_t p_code_section)
  {
    for (size_t i = 0; i < v.size(); ++i) {
      v[i]->get_var_ref()->set_code_section(p_code_section);
    }
  }
  
  void ValueRedirect::add(SingleValueRedirect* ptr)
  {
    if (ptr == NULL) {
      FATAL_ERROR("ValueRedirect::add");
    }
    v.add(ptr);
  }
  
  Type* ValueRedirect::get_type() const
  {
    Error_Context cntxt(this, "In value redirect");
    Type* ret_val = NULL;
    for (size_t i = 0; i < v.size(); ++i) {
      if (v[i]->get_subrefs() == NULL) {
        Type* var_type = v[i]->get_var_ref()->chk_variable_ref();
        if (var_type != NULL) {
          if (ret_val == NULL) {
            ret_val = var_type;
          }
          else {
            if (!ret_val->is_identical(var_type)) {
              error("The variable references the whole value is redirected to "
                "should be of the same type");
              return NULL;
            }
          }
        }
      }
    }
    return ret_val;
  }
  
  bool ValueRedirect::chk_RT1_restrictions() const
  {
    if (v.size() > 1) {
      error(verdict_only ? "Only one redirect is allowed in this case." :
        "Redirecting multiple values is not supported in the Load Test Runtime.");
      return false;
    }
    if (v[0]->get_subrefs() != NULL) {
      error(verdict_only ? "Cannot apply field names or array indexes to a "
        "variable of type `verdicttype'." :
        "Redirecting parts of a value is not supported in the Load Test Runtime.");
      return false;
    }
    return true;
  }
  
  void ValueRedirect::chk_erroneous()
  {
    Error_Context cntxt(this, "In value redirect");
    if (!use_runtime_2 && !chk_RT1_restrictions()) {
      return;
    }
    for (size_t i = 0; i < v.size(); ++i) {
      Error_Context cntxt2(v[i], "In redirect #%d", static_cast<int>(i + 1));
      v[i]->get_var_ref()->chk_variable_ref();
      Value* str_enc = v[i]->get_str_enc();
      if (str_enc != NULL) {
        str_enc->chk_string_encoding(NULL);
      }
    }
  }
  
  void ValueRedirect::chk_verdict_only()
  {
    verdict_only = true;
    chk(Type::get_pooltype(Type::T_VERDICT));
  }
  
  void ValueRedirect::chk(Type* p_type)
  {
    if ((verdict_only || !use_runtime_2) && !chk_RT1_restrictions()) {
      return;
    }
    bool invalid_type = p_type->get_typetype() == Type::T_ERROR;
    if (!invalid_type) {
      // initial check: redirects of fields require the value type to be a record
      // or set
      Type::typetype_t tt = p_type->get_type_refd_last()->get_typetype_ttcn3();
      Error_Context cntxt(this, "In value redirect");
      if (tt != Type::T_SEQ_T && tt != Type::T_SET_T) {
        for (size_t i = 0; i < v.size(); ++i) {
          if (v[i]->get_subrefs() != NULL) {
            invalid_type = true;
            Error_Context cntxt2(v[i], "In redirect #%d", static_cast<int>(i + 1));
            v[i]->error("Cannot redirect fields of type '%s', because it is not a "
              "record or set", p_type->get_typename().c_str());
          }
        }
      }
    }
    if (invalid_type) {
      chk_erroneous();
      return;
    }
    value_type = p_type->get_type_refd_last();
    Error_Context cntxt(this, "In value redirect");
    for (size_t i = 0; i < v.size(); ++i) {
      Reference* var_ref = v[i]->get_var_ref();
      Type* var_type = var_ref->chk_variable_ref();
      FieldOrArrayRefs* subrefs = v[i]->get_subrefs();
      Error_Context cntxt2(v[i], "In redirect #%d", static_cast<int>(i + 1));
      Type* exp_type = NULL;
      if (subrefs != NULL) {
        // a field of the value is redirected to the referenced variable
        Type* field_type = p_type->get_field_type(subrefs, Type::EXPECTED_DYNAMIC_VALUE);
        if (field_type != NULL) {
          if (v[i]->is_decoded()) {
            Value* str_enc = v[i]->get_str_enc();
            bool erroneous = false;
            switch (field_type->get_type_refd_last()->get_typetype_ttcn3()) {
            case Type::T_BSTR:
            case Type::T_HSTR:
            case Type::T_OSTR:
            case Type::T_CSTR:
              if (str_enc != NULL) {
                str_enc->error("The encoding format parameter for the '@decoded' modifier "
                  "is only available to value redirects of universal charstrings");
                erroneous = true;
              }
              break;
            case Type::T_USTR:
              if (str_enc != NULL) {
                str_enc->chk_string_encoding(NULL);
              }
              break;
            default:
              v[i]->error("The '@decoded' modifier is only available to value "
                "redirects of string types.");
              erroneous = true;
              break;
            }
            if (!erroneous && var_type != NULL) {
              // store the variable type in case it's decoded (since this cannot
              // be extracted from the value type with the sub-references)
              Type* dec_type = var_type->get_type_refd();
              v[i]->set_dec_type(dec_type);
              dec_type->chk_coding(false,
                var_ref->get_my_scope()->get_scope_mod());
            }
          }
          else {
            exp_type = field_type;
          }
        }
      }
      else {
        // the whole value is redirected to the referenced variable
        exp_type = p_type;
      }
      if (exp_type != NULL && var_type != NULL) {
        if (use_runtime_2 && !verdict_only) {
          // check for type compatibility in RT2
          TypeCompatInfo info(v[i]->get_var_ref()->get_my_scope()->get_scope_mod(),
            exp_type, var_type, true, false);
          FieldOrArrayRefs* var_subrefs = var_ref->get_subrefs();
          if (var_subrefs != NULL) {
            info.set_str2_elem(var_subrefs->refers_to_string_element());
          }
          TypeChain l_chain;
          TypeChain r_chain;
          if (!exp_type->is_compatible(var_type, &info, this, &l_chain, &r_chain)) {
            if (info.is_subtype_error()) {
              v[i]->error("%s", info.get_subtype_error().c_str());
            }
            else if (!info.is_erroneous()) {
              v[i]->error("Type mismatch in value redirect: A variable of type "
                "`%s' or of a compatible type was expected instead of `%s'",
                exp_type->get_typename().c_str(), var_type->get_typename().c_str());
            }
            else {
              v[i]->error("%s", info.get_error_str_str().c_str());
            }
          }
        }
        else if (!var_type->is_identical(exp_type)) {
          // the types have to be identical in RT1
          v[i]->error("Type mismatch in value redirect: A variable of type "
            "`%s' was expected instead of `%s'",
            exp_type->get_typename().c_str(), var_type->get_typename().c_str());
        }
      }
    }
  }
  
  void ValueRedirect::generate_code(expression_struct* expr,
                                    TemplateInstance* matched_ti)
  {
    if (use_runtime_2 && !verdict_only) {
      // a value redirect class is generated for this redirect in the expression's
      // preamble and instantiated in the expression
      Scope* scope = v[0]->get_var_ref()->get_my_scope();
      string class_id = scope->get_scope_mod_gen()->get_temporary_id();
      string instance_id = scope->get_scope_mod_gen()->get_temporary_id();

      // go through the value redirect and gather the necessary data for the class
      char* members_str = NULL;
      char* constr_params_str = NULL;
      char* constr_init_list_str = NULL;
      char* set_values_str = NULL;
      char* inst_params_str = NULL;
      if (matched_ti != NULL && has_decoded_modifier()) {
        // store a pointer to the matched template, the decoding results from
        // decmatch templates might be reused to optimize decoded value redirects
        inst_params_str = mprintf("&(%s), ", matched_ti->get_last_gen_expr());
        members_str = mprintf("%s* ptr_matched_temp;\n",
          value_type->get_genname_template(scope).c_str());
        constr_params_str = mputprintf(constr_params_str, "%s* par_matched_temp, ",
          value_type->get_genname_template(scope).c_str());
        constr_init_list_str = mcopystr("ptr_matched_temp(par_matched_temp), ");
      }
      boolean need_par = FALSE;
      for (size_t i = 0; i < v.size(); ++i) {
        if (i > 0) {
          inst_params_str = mputstr(inst_params_str, ", ");
          constr_params_str = mputstr(constr_params_str, ", ");
          constr_init_list_str = mputstr(constr_init_list_str, ", ");
        }
        // pass the variable references to the new instance's constructor
        expression_struct var_ref_expr;
        Code::init_expr(&var_ref_expr);
        inst_params_str = mputstr(inst_params_str, "&(");
        Reference* ref = v[i]->get_var_ref();
        ref->generate_code(&var_ref_expr);
        inst_params_str = mputstr(inst_params_str, var_ref_expr.expr);
        if (ref->get_refd_assignment()->get_Type()->get_type_refd_last()->
            get_field_type(ref->get_subrefs(),
            Common::Type::EXPECTED_DYNAMIC_VALUE)->is_optional_field()) {
          inst_params_str = mputstr(inst_params_str, "()");
        }
        inst_params_str = mputc(inst_params_str, ')');
        if (var_ref_expr.preamble != NULL) {
          expr->preamble = mputstr(expr->preamble, var_ref_expr.preamble);
        }
        if (var_ref_expr.postamble != NULL) {
          expr->postamble = mputstr(expr->postamble, var_ref_expr.postamble);
        }
        Code::free_expr(&var_ref_expr);
        Type* redir_type = v[i]->get_subrefs() == NULL ? value_type :
          value_type->get_field_type(v[i]->get_subrefs(), Type::EXPECTED_DYNAMIC_VALUE);
        redir_type = redir_type->get_type_refd_last();
        Type* ref_type = v[i]->get_var_ref()->chk_variable_ref()->get_type_refd_last();
        Type* member_type = v[i]->is_decoded() ? v[i]->get_dec_type() : ref_type;
        string type_str = member_type->get_genname_value(scope);
        members_str = mputprintf(members_str, "%s* ptr_%d;\n", type_str.c_str(), static_cast<int>(i));
        constr_params_str = mputprintf(constr_params_str, "%s* par_%d",
          type_str.c_str(), static_cast<int>(i));
        constr_init_list_str = mputprintf(constr_init_list_str,
          "ptr_%d(par_%d)", static_cast<int>(i), static_cast<int>(i));
        // generate the sub-references' code in a separate expression structure and
        // insert it into the set_values function
        expression_struct subrefs_expr;
        Code::init_expr(&subrefs_expr);
        const char* opt_suffix = "";
        if (v[i]->get_subrefs() != NULL) {
          v[i]->get_subrefs()->generate_code(&subrefs_expr, value_type, scope);
          if (redir_type->get_ownertype() == Type::OT_COMP_FIELD) {
            CompField* cf = static_cast<CompField*>( redir_type->get_owner() );
            if (cf->get_is_optional()) {
              opt_suffix = "()";
            }
          }
        }
        if (subrefs_expr.preamble != NULL) {
          set_values_str = mputstr(set_values_str, subrefs_expr.preamble);
        }
        const char* subrefs_str = (subrefs_expr.expr != NULL) ? subrefs_expr.expr : "";
        if (v[i]->is_decoded()) {
          // set the silent parameter to 'true', so no errors are displayed
          Template* matched_temp = matched_ti != NULL ?
            matched_ti->get_Template()->get_refd_sub_template(v[i]->get_subrefs(),
            false, NULL, true) : NULL;
          if (matched_temp != NULL) {
            matched_temp = matched_temp->get_template_refd_last();
          }
          bool use_decmatch_result = matched_temp != NULL &&
            matched_temp->get_templatetype() == Template::DECODE_MATCH;
          bool needs_decode = true;
          expression_struct redir_coding_expr;
          Code::init_expr(&redir_coding_expr);
          if (redir_type->get_type_refd_last()->get_typetype_ttcn3() == Type::T_USTR) {
            if (v[i]->get_str_enc() == NULL || !v[i]->get_str_enc()->is_unfoldable()) {
              const char* redir_coding_str;
              if (v[i]->get_str_enc() == NULL ||
                  v[i]->get_str_enc()->get_val_str() == "UTF-8") {
                redir_coding_str = "UTF_8";
              }
              else if (v[i]->get_str_enc()->get_val_str() == "UTF-16" ||
                       v[i]->get_str_enc()->get_val_str() == "UTF-16BE") {
                redir_coding_str = "UTF16BE";
              }
              else if (v[i]->get_str_enc()->get_val_str() == "UTF-16LE") {
                redir_coding_str = "UTF16LE";
              }
              else if (v[i]->get_str_enc()->get_val_str() == "UTF-32LE") {
                redir_coding_str = "UTF32LE";
              }
              else {
                redir_coding_str = "UTF32BE";
              }
              redir_coding_expr.expr = mprintf("CharCoding::%s", redir_coding_str);
            }
            else {
              redir_coding_expr.preamble = mprintf(
                "CharCoding::CharCodingType coding = UNIVERSAL_CHARSTRING::"
                "get_character_coding(enc_fmt_%d, \"decoded parameter redirect\");\n",
                static_cast<int>(i));
              redir_coding_expr.expr = mcopystr("coding");
            }
          }
          if (use_decmatch_result) {
            // if the redirected value was matched using a decmatch template,
            // then the value redirect class should use the decoding result 
            // from the template instead of decoding the value again
            needs_decode = false;
            Type* decmatch_type = matched_temp->get_decode_target()->get_expr_governor(
              Type::EXPECTED_TEMPLATE)->get_type_refd_last();
            if (v[i]->get_dec_type() != decmatch_type) {
              // the decmatch template and this value redirect decode two
              // different types, so just decode the value
              needs_decode = true;
              use_decmatch_result = false;
            }
            else if (redir_type->get_type_refd_last()->get_typetype_ttcn3() ==
                     Type::T_USTR) {
              // for universal charstrings the situation could be trickier
              // compare the string encodings
              bool different_ustr_encodings = false;
              bool unknown_ustr_encodings = false;
              if (v[i]->get_str_enc() == NULL) {
                if (matched_temp->get_string_encoding() != NULL) {
                  if (matched_temp->get_string_encoding()->is_unfoldable()) {
                    unknown_ustr_encodings = true;
                  }
                  else if (matched_temp->get_string_encoding()->get_val_str() != "UTF-8") {
                    different_ustr_encodings = true;
                  }
                }
              }
              else if (v[i]->get_str_enc()->is_unfoldable()) {
                unknown_ustr_encodings = true;
              }
              else if (matched_temp->get_string_encoding() == NULL) {
                if (v[i]->get_str_enc()->get_val_str() != "UTF-8") {
                  different_ustr_encodings = true;
                }
              }
              else if (matched_temp->get_string_encoding()->is_unfoldable()) {
                unknown_ustr_encodings = true;
              }
              else if (v[i]->get_str_enc()->get_val_str() !=
                       matched_temp->get_string_encoding()->get_val_str()) {
                different_ustr_encodings = true;
              }
              if (unknown_ustr_encodings) {
                // the decision of whether to use the decmatch result or to decode
                // the value is made at runtime
                needs_decode = true;
                set_values_str = mputprintf(set_values_str,
                  "%sif (%s == (*ptr_matched_temp)%s.get_decmatch_str_enc()) {\n",
                  redir_coding_expr.preamble != NULL ? redir_coding_expr.preamble : "",
                  redir_coding_expr.expr, subrefs_str);
              }
              else if (different_ustr_encodings) {
                // if the encodings are different, then ignore the decmatch result
                // and just generate the decoding code as usual
                needs_decode = true;
                use_decmatch_result = false;
              }
            }
          }
          else {
            // it might still be a decmatch template if it's not known at compile-time
            bool unfoldable = matched_temp == NULL;
            if (!unfoldable) {
              switch (matched_temp->get_templatetype()) {
              case Template::ANY_VALUE:
              case Template::ANY_OR_OMIT:
              case Template::BSTR_PATTERN:
              case Template::CSTR_PATTERN:
              case Template::HSTR_PATTERN:
              case Template::OSTR_PATTERN:
              case Template::USTR_PATTERN:
              case Template::COMPLEMENTED_LIST:
              case Template::VALUE_LIST:
              case Template::VALUE_RANGE:
              case Template::OMIT_VALUE:
                // it's known at compile-time, and not a decmatch template
                break;
              default:
                // needs runtime check
                unfoldable = true;
                break;
              }
            }
            if (unfoldable && matched_ti != NULL) {
              // the decmatch-check must be done at runtime
              use_decmatch_result = true;
              if (redir_coding_expr.preamble != NULL) {
                set_values_str = mputstr(set_values_str, redir_coding_expr.preamble);
              }
              // before we can check whether the template at the end of the
              // subreferences is a decmatch template, we must make sure that
              // every prior subreference points to a specific value template
              // (otherwise accessing the template at the end will result in a DTE)
              set_values_str = mputstr(set_values_str,
                "if (ptr_matched_temp->get_selection() == SPECIFIC_VALUE && ");
              char* current_ref = mcopystr("(*ptr_matched_temp)");
              size_t len = strlen(subrefs_str);
              size_t start = 0;
              // go through the already generated subreference string, append
              // one reference at a time, and check if the referenced template
              // is a specific value
              for (size_t j = 1; j < len; ++j) {
                if (subrefs_str[j] == '.' || subrefs_str[j] == '[') {
                  current_ref = mputstrn(current_ref, subrefs_str + start, j - start);
                  set_values_str = mputprintf(set_values_str,
                    "%s.get_selection() == SPECIFIC_VALUE && ", current_ref);
                  start = j;
                }
              }
              Free(current_ref);
              set_values_str = mputprintf(set_values_str,
                "(*ptr_matched_temp)%s.get_selection() == DECODE_MATCH && "
                // the type the value was decoded to in the template must be the same
                // as the type this value redirect would decode the redirected value to
                // (this is checked by comparing the addresses of the type descriptors)
                "&%s_descr_ == (*ptr_matched_temp)%s.get_decmatch_type_descr()",
                subrefs_str,
                v[i]->get_dec_type()->get_genname_typedescriptor(scope).c_str(),
                subrefs_str);
              if (redir_coding_expr.expr != NULL) {
                set_values_str = mputprintf(set_values_str,
                  " && %s == (*ptr_matched_temp)%s.get_decmatch_str_enc()",
                  redir_coding_expr.expr, subrefs_str);
              }
              set_values_str = mputstr(set_values_str, ") {\n");
            }
          }
          Code::free_expr(&redir_coding_expr);
          if (use_decmatch_result) {
            set_values_str = mputprintf(set_values_str,
              "*ptr_%d = *((%s*)((*ptr_matched_temp)%s.get_decmatch_dec_res()));\n",
              static_cast<int>(i), type_str.c_str(), subrefs_str);
          }
          if (needs_decode) {
            need_par = TRUE;
            if (use_decmatch_result) {
              set_values_str = mputstr(set_values_str, "}\nelse {\n");
            }
            Type::typetype_t tt = redir_type->get_type_refd_last()->get_typetype_ttcn3();
            if (legacy_codec_handling && member_type->is_coding_by_function(false)) {
              set_values_str = mputprintf(set_values_str, "BITSTRING buff_%d(",
                static_cast<int>(i));
              switch (tt) {
              case Type::T_BSTR:
                set_values_str = mputprintf(set_values_str, "(*par)%s%s",
                  subrefs_str, opt_suffix);
                break;
              case Type::T_HSTR:
                set_values_str = mputprintf(set_values_str, "hex2bit((*par)%s%s)",
                  subrefs_str, opt_suffix);
                break;
              case Type::T_OSTR:
                set_values_str = mputprintf(set_values_str, "oct2bit((*par)%s%s)",
                  subrefs_str, opt_suffix);
                break;
              case Type::T_CSTR:
                set_values_str = mputprintf(set_values_str,
                  "oct2bit(char2oct((*par)%s%s))", subrefs_str, opt_suffix);
                break;
              case Type::T_USTR:
                set_values_str = mputprintf(set_values_str,
                  "oct2bit(unichar2oct((*par)%s%s, ", subrefs_str, opt_suffix);
                if (v[i]->get_str_enc() == NULL || !v[i]->get_str_enc()->is_unfoldable()) {
                  // encoding format is missing or is known at compile-time
                  set_values_str = mputprintf(set_values_str, "\"%s\"",
                    v[i]->get_str_enc() != NULL ?
                    v[i]->get_str_enc()->get_val_str().c_str() : "UTF-8");
                }
                else {
                  // the encoding format is not known at compile-time, so an extra
                  // member and constructor parameter is needed to store it
                  inst_params_str = mputstr(inst_params_str, ", ");
                  expression_struct str_enc_expr;
                  Code::init_expr(&str_enc_expr);
                  v[i]->get_str_enc()->generate_code_expr(&str_enc_expr);
                  inst_params_str = mputstr(inst_params_str, str_enc_expr.expr);
                  if (str_enc_expr.preamble != NULL) {
                    expr->preamble = mputstr(expr->preamble, str_enc_expr.preamble);
                  }
                  if (str_enc_expr.postamble != NULL) {
                    expr->postamble = mputstr(expr->postamble, str_enc_expr.postamble);
                  }
                  Code::free_expr(&str_enc_expr);
                  members_str = mputprintf(members_str, "CHARSTRING enc_fmt_%d;\n",
                    static_cast<int>(i));
                  constr_params_str = mputprintf(constr_params_str,
                    ", CHARSTRING par_fmt_%d", static_cast<int>(i));
                  constr_init_list_str = mputprintf(constr_init_list_str,
                    ", enc_fmt_%d(par_fmt_%d)", static_cast<int>(i), static_cast<int>(i));
                  set_values_str = mputprintf(set_values_str, "enc_fmt_%d", static_cast<int>(i));
                }
                set_values_str = mputstr(set_values_str, "))");
                break;
              default:
                FATAL_ERROR("ValueRedirect::generate_code");
              }
              set_values_str = mputprintf(set_values_str,
                ");\n"
                "if (%s(buff_%d, *ptr_%d) != 0) {\n"
                "TTCN_error(\"Decoding failed in value redirect #%d.\");\n"
                "}\n"
                "if (buff_%d.lengthof() != 0) {\n"
                "TTCN_error(\"Value redirect #%d failed, because the buffer was "
                "not empty after decoding. Remaining bits: %%d.\", "
                "buff_%d.lengthof());\n"
                "}\n", member_type->get_legacy_coding_function(false)->
                get_genname_from_scope(scope).c_str(),
                static_cast<int>(i), static_cast<int>(i), static_cast<int>(i + 1),
                static_cast<int>(i), static_cast<int>(i + 1), static_cast<int>(i));
            }
            else if (legacy_codec_handling) { // legacy codec handing with built-in decoding
              switch (tt) {
              case Type::T_OSTR:
              case Type::T_CSTR:
                set_values_str = mputprintf(set_values_str,
                  "TTCN_Buffer buff_%d((*par)%s%s);\n", static_cast<int>(i), subrefs_str, opt_suffix);
                break;
              case Type::T_BSTR:
                set_values_str = mputprintf(set_values_str,
                  "OCTETSTRING os(bit2oct((*par)%s%s));\n"
                  "TTCN_Buffer buff_%d(os);\n", subrefs_str, opt_suffix, static_cast<int>(i));
                break;
              case Type::T_HSTR:
                set_values_str = mputprintf(set_values_str,
                  "OCTETSTRING os(hex2oct((*par)%s%s));\n"
                  "TTCN_Buffer buff_%d(os);\n", subrefs_str, opt_suffix, static_cast<int>(i));
                break;
              case Type::T_USTR:
                set_values_str = mputprintf(set_values_str, "TTCN_Buffer buff_%d;\n",
                  static_cast<int>(i));
                if (v[i]->get_str_enc() == NULL || !v[i]->get_str_enc()->is_unfoldable()) {
                  // if the encoding format is missing or is known at compile-time, then
                  // use the appropriate string encoding function
                  string str_enc = (v[i]->get_str_enc() != NULL) ?
                    v[i]->get_str_enc()->get_val_str() : string("UTF-8");
                  if (str_enc == "UTF-8") {
                    set_values_str = mputprintf(set_values_str,
                      "(*par)%s%s.encode_utf8(buff_%d, false);\n", subrefs_str, opt_suffix, static_cast<int>(i));
                  }
                  else if (str_enc == "UTF-16" || str_enc == "UTF-16LE" ||
                           str_enc == "UTF-16BE") {
                    set_values_str = mputprintf(set_values_str,
                      "(*par)%s%s.encode_utf16(buff_%d, CharCoding::UTF16%s);\n", subrefs_str,
                      opt_suffix, static_cast<int>(i), (str_enc == "UTF-16LE") ? "LE" : "BE");
                  }
                  else if (str_enc == "UTF-32" || str_enc == "UTF-32LE" ||
                           str_enc == "UTF-32BE") {
                    set_values_str = mputprintf(set_values_str,
                      "(*par)%s%s.encode_utf32(buff_%d, CharCoding::UTF32%s);\n", subrefs_str,
                      opt_suffix, static_cast<int>(i), (str_enc == "UTF-32LE") ? "LE" : "BE");
                  }
                }
                else {
                  // the encoding format is not known at compile-time, so an extra
                  // member and constructor parameter is needed to store it
                  inst_params_str = mputstr(inst_params_str, ", ");
                  expression_struct str_enc_expr;
                  Code::init_expr(&str_enc_expr);
                  v[i]->get_str_enc()->generate_code_expr(&str_enc_expr);
                  inst_params_str = mputstr(inst_params_str, str_enc_expr.expr);
                  if (str_enc_expr.preamble != NULL) {
                    expr->preamble = mputstr(expr->preamble, str_enc_expr.preamble);
                  }
                  if (str_enc_expr.postamble != NULL) {
                    expr->postamble = mputstr(expr->postamble, str_enc_expr.postamble);
                  }
                  Code::free_expr(&str_enc_expr);
                  members_str = mputprintf(members_str, "CHARSTRING enc_fmt_%d;\n",
                    static_cast<int>(i));
                  constr_params_str = mputprintf(constr_params_str,
                    ", CHARSTRING par_fmt_%d", static_cast<int>(i));
                  constr_init_list_str = mputprintf(constr_init_list_str,
                    ", enc_fmt_%d(par_fmt_%d)", static_cast<int>(i), static_cast<int>(i));
                  if (!use_decmatch_result) {
                    // if the decmatch result code is generated too, then this variable
                    // was already generated before the main 'if'
                    set_values_str = mputprintf(set_values_str,
                      "CharCoding::CharCodingType coding = UNIVERSAL_CHARSTRING::"
                      "get_character_coding(enc_fmt_%d, \"decoded value redirect\");\n",
                      static_cast<int>(i));
                  }
                  set_values_str = mputprintf(set_values_str,
                    "switch (coding) {\n"
                    "case CharCoding::UTF_8:\n"
                    "(*par)%s%s.encode_utf8(buff_%d, false);\n"
                    "break;\n"
                    "case CharCoding::UTF16:\n"
                    "case CharCoding::UTF16LE:\n"
                    "case CharCoding::UTF16BE:\n"
                    "(*par)%s%s.encode_utf16(buff_%d, coding);\n"
                    "break;\n"
                    "case CharCoding::UTF32:\n"
                    "case CharCoding::UTF32LE:\n"
                    "case CharCoding::UTF32BE:\n"
                    "(*par)%s%s.encode_utf32(buff_%d, coding);\n"
                    "break;\n"
                    "default:\n"
                    "break;\n"
                    "}\n", subrefs_str, opt_suffix, static_cast<int>(i), subrefs_str, opt_suffix,
                    static_cast<int>(i), subrefs_str, opt_suffix, static_cast<int>(i));
                }
                break;
              default:
                FATAL_ERROR("ValueRedirect::generate_code");
              }
              set_values_str = mputprintf(set_values_str,
                "ptr_%d->decode(%s_descr_, buff_%d, TTCN_EncDec::CT_%s);\n"
                "if (buff_%d.get_read_len() != 0) {\n"
                "TTCN_error(\"Value redirect #%d failed, because the buffer was "
                "not empty after decoding. Remaining octets: %%d.\", "
                "static_cast<int>( buff_%d.get_read_len() ));\n"
                "}\n",
                static_cast<int>(i), member_type->get_genname_typedescriptor(scope).c_str(), static_cast<int>(i),
                member_type->get_coding(false).c_str(), static_cast<int>(i), static_cast<int>(i + 1), static_cast<int>(i));
            }
            else { // new codec handling
              set_values_str = mputprintf(set_values_str, "OCTETSTRING buff_%d(",
                static_cast<int>(i));
              switch (tt) {
              case Type::T_BSTR:
                set_values_str = mputprintf(set_values_str, "bit2oct((*par)%s%s)",
                  subrefs_str, opt_suffix);
                break;
              case Type::T_HSTR:
                set_values_str = mputprintf(set_values_str, "hex2oct((*par)%s%s)",
                  subrefs_str, opt_suffix);
                break;
              case Type::T_OSTR:
                set_values_str = mputprintf(set_values_str, "(*par)%s%s",
                  subrefs_str, opt_suffix);
                break;
              case Type::T_CSTR:
                set_values_str = mputprintf(set_values_str,
                  "char2oct((*par)%s%s)", subrefs_str, opt_suffix);
                break;
              case Type::T_USTR:
                set_values_str = mputprintf(set_values_str,
                  "unichar2oct((*par)%s%s, ", subrefs_str, opt_suffix);
                if (v[i]->get_str_enc() == NULL || !v[i]->get_str_enc()->is_unfoldable()) {
                  // encoding format is missing or is known at compile-time
                  set_values_str = mputprintf(set_values_str, "\"%s\"",
                    v[i]->get_str_enc() != NULL ?
                    v[i]->get_str_enc()->get_val_str().c_str() : "UTF-8");
                }
                else {
                  // the encoding format is not known at compile-time, so an extra
                  // member and constructor parameter is needed to store it
                  inst_params_str = mputstr(inst_params_str, ", ");
                  expression_struct str_enc_expr;
                  Code::init_expr(&str_enc_expr);
                  v[i]->get_str_enc()->generate_code_expr(&str_enc_expr);
                  inst_params_str = mputstr(inst_params_str, str_enc_expr.expr);
                  if (str_enc_expr.preamble != NULL) {
                    expr->preamble = mputstr(expr->preamble, str_enc_expr.preamble);
                  }
                  if (str_enc_expr.postamble != NULL) {
                    expr->postamble = mputstr(expr->postamble, str_enc_expr.postamble);
                  }
                  Code::free_expr(&str_enc_expr);
                  members_str = mputprintf(members_str, "CHARSTRING enc_fmt_%d;\n",
                    static_cast<int>(i));
                  constr_params_str = mputprintf(constr_params_str,
                    ", CHARSTRING par_fmt_%d", static_cast<int>(i));
                  constr_init_list_str = mputprintf(constr_init_list_str,
                    ", enc_fmt_%d(par_fmt_%d)", static_cast<int>(i), static_cast<int>(i));
                  set_values_str = mputprintf(set_values_str, "enc_fmt_%d", static_cast<int>(i));
                }
                set_values_str = mputstr(set_values_str, ")");
                break;
              default:
                FATAL_ERROR("ValueRedirect::generate_code");
              }
              set_values_str = mputprintf(set_values_str,
                ");\n"
                "if (%s_decoder(buff_%d, *ptr_%d, %s_default_coding) != 0) {\n"
                "TTCN_error(\"Decoding failed in value redirect #%d.\");\n"
                "}\n"
                "if (buff_%d.lengthof() != 0) {\n"
                "TTCN_error(\"Value redirect #%d failed, because the buffer was "
                "not empty after decoding. Remaining octets: %%d.\", "
                "buff_%d.lengthof());\n"
                "}\n",
                member_type->get_genname_coder(scope).c_str(),
                static_cast<int>(i), static_cast<int>(i),
                member_type->get_genname_default_coding(scope).c_str(),
                static_cast<int>(i + 1), static_cast<int>(i),
                static_cast<int>(i + 1), static_cast<int>(i));
            }
            if (use_decmatch_result) {
              set_values_str = mputstr(set_values_str, "}\n");
            }
          }
        }
        else {
          need_par = TRUE;
          // if the variable reference and the received value (or its specified field)
          // are not of the same type, then a type conversion is needed (RT2 only)
          if (!ref_type->is_identical(redir_type)) {
            Common::Module* mod = scope->get_scope_mod();
            mod->add_type_conv(new TypeConv(redir_type, ref_type, false));
            set_values_str = mputprintf(set_values_str,
              "if (!%s(*ptr_%d, (*par)%s%s)) {\n"
              "TTCN_error(\"Failed to convert redirected value #%d from type `%s' "
              "to type `%s'.\");\n"
              "}\n",
              TypeConv::get_conv_func(redir_type, ref_type, mod).c_str(), static_cast<int>(i),
              subrefs_str, opt_suffix, static_cast<int>(i + 1), redir_type->get_typename().c_str(),
              ref_type->get_typename().c_str());
          }
          else {
            set_values_str = mputprintf(set_values_str, "*ptr_%d = (*par)%s%s;\n",
              static_cast<int>(i), subrefs_str, opt_suffix);
          }
        }
        if (subrefs_expr.postamble != NULL) {
          set_values_str = mputstr(set_values_str, subrefs_expr.postamble);
        }
        Code::free_expr(&subrefs_expr);
      }
      // generate the new class with the gathered data
      expr->preamble = mputprintf(expr->preamble,
        "class Value_Redirect_%s : public Value_Redirect_Interface {\n"
        // member declarations; one for each variable reference
        "%s"
        "public:\n"
        // constructor:
        // stores the pointers to the target variables in the class' members
        "Value_Redirect_%s(%s)\n"
        ": %s { }\n"
        // set_values function: assigns the whole value or a part of it to each
        // variable; the redirects marked with the '@decoded' modifier are decoded
        // here before they are assigned
        "void set_values(const Base_Type*%s)\n"
        "{\n", class_id.c_str(), members_str, class_id.c_str(), constr_params_str,
        constr_init_list_str, need_par ? " p" : "");
      if (need_par) {
        // don't generate the parameter and its casting if it is never used
        expr->preamble = mputprintf(expr->preamble,
          "const %s* par = static_cast<const %s*>(p);\n",
          value_type->get_genname_value(scope).c_str(), 
          value_type->get_genname_value(scope).c_str());
      }
      expr->preamble = mputstr(expr->preamble, set_values_str);
      expr->preamble = mputprintf(expr->preamble,
        "}\n"
        "};\n"
        "Value_Redirect_%s %s(%s);\n",
        class_id.c_str(), instance_id.c_str(), inst_params_str);
      Free(members_str);
      Free(constr_params_str);
      Free(constr_init_list_str);
      Free(set_values_str);
      Free(inst_params_str);
      expr->expr = mputprintf(expr->expr, "&%s", instance_id.c_str());
    }
    else { // RT1 or verdict only
      // in this case only the address of the one variable needs to be generated
      expr->expr = mputstr(expr->expr, "&(");
      v[0]->get_var_ref()->generate_code(expr);
      expr->expr = mputc(expr->expr, ')');
    }
  }
  
  bool ValueRedirect::has_decoded_modifier() const
  {
    for (size_t i = 0; i < v.size(); ++i) {
      if (v[i]->is_decoded()) {
        return true;
      }
    }
    return false;
  }

  // =================================
  // ===== LogArgument
  // =================================

  LogArgument::LogArgument(TemplateInstance *p_ti)
    : logargtype(L_UNDEF)
  {
    if (!p_ti) FATAL_ERROR("LogArgument::LogArgument()");
    ti = p_ti;
  }
  
  LogArgument::LogArgument(const LogArgument& p) : logargtype(p.logargtype)
  {
    switch (logargtype) {
    case L_ERROR:
      break;
    case L_UNDEF:
    case L_TI:
      ti = p.ti->clone();
      break;
    case L_VAL:
    case L_MATCH:
    case L_MACRO:
      val = p.val->clone();
      break;
    case L_REF:
      ref = p.ref->clone();
      break;
    case L_STR:
      cstr = new string(*cstr);
      break;
    default:
      FATAL_ERROR("LogArgument::LogArgument()");
    } // switch
  }

  LogArgument::~LogArgument()
  {
    switch (logargtype) {
    case L_ERROR:
      break;
    case L_UNDEF:
    case L_TI:
      delete ti;
      break;
    case L_VAL:
    case L_MATCH:
    case L_MACRO:
      delete val;
      break;
    case L_REF:
      delete ref;
      break;
    case L_STR:
      delete cstr;
      break;
    default:
      FATAL_ERROR("LogArgument::~LogArgument()");
    } // switch
  }

  LogArgument *LogArgument::clone() const
  {
    return new LogArgument(*this);
  }

  void LogArgument::set_my_scope(Scope *p_scope)
  {
    switch (logargtype) {
    case L_ERROR:
      break;
    case L_UNDEF:
    case L_TI:
      ti->set_my_scope(p_scope);
      break;
    case L_VAL:
    case L_MATCH:
    case L_MACRO:
      val->set_my_scope(p_scope);
      break;
    case L_REF:
      ref->set_my_scope(p_scope);
      break;
    case L_STR:
      break;
    default:
      FATAL_ERROR("LogArgument::set_my_scope()");
    } // switch
  }

  void LogArgument::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    switch (logargtype) {
    case L_ERROR:
      break;
    case L_UNDEF:
    case L_TI:
      ti->set_fullname(p_fullname);
      break;
    case L_VAL:
    case L_MATCH:
    case L_MACRO:
      val->set_fullname(p_fullname);
      break;
    case L_REF:
      ref->set_fullname(p_fullname);
      break;
    case L_STR:
      break;
    default:
      FATAL_ERROR("LogArgument::set_fullname()");
    } // switch
  }

  const string& LogArgument::get_str() const
  {
    if (logargtype != L_STR) FATAL_ERROR("LogArgument::get_str()");
    return *cstr;
  }

  Value *LogArgument::get_val() const
  {
    switch (logargtype) {
    case L_VAL:
    case L_MATCH:
    case L_MACRO:
      break;
    default:
      FATAL_ERROR("LogArgument::get_val()");
    }
    return val;
  }

  Reference *LogArgument::get_ref() const
  {
    if (logargtype != L_REF) FATAL_ERROR("LogArgument::get_ref()");
    return ref;
  }

  TemplateInstance *LogArgument::get_ti() const
  {
    if (logargtype != L_TI) FATAL_ERROR("LogArgument::get_ref()");
    return ti;
  }

  void LogArgument::append_str(const string& p_str)
  {
    if (logargtype != L_STR) FATAL_ERROR("LogArgument::append_str()");
    *cstr += p_str;
  }

  void LogArgument::chk() // determine the proper type of the log argument
  {
    if (logargtype != L_UNDEF) return;
    Template *t_templ = ti->get_Template();
    t_templ = t_templ->get_template_refd_last();
    t_templ->set_lowerid_to_ref();
    if (!ti->get_Type() && !ti->get_DerivedRef() && t_templ->is_Value()) {
      // drop the template instance and keep only the embedded value
      Value *t_val = t_templ->get_Value();
      delete ti;
      logargtype = L_VAL;
      val = t_val;
      chk_val();
    } else {
      // try to obtain the governor of the template instance
      Type *governor = ti->get_expr_governor(Type::EXPECTED_TEMPLATE);
      if (!governor) {
	// the governor is still unknown: an error occurred
	// try to interpret possible enum values as references
	t_templ->set_lowerid_to_ref();
	governor = t_templ->get_expr_governor(Type::EXPECTED_TEMPLATE);
      }
      if (governor) {
	logargtype = L_TI;
	ti->chk(governor);
      } else {
	t_templ->error("Cannot determine the type of the argument");
	delete ti;
	logargtype = L_ERROR;
      }
    }
  }

  void LogArgument::chk_ref()
  {
    Common::Assignment *t_ass = ref->get_refd_assignment();
    if (!t_ass) return;
    Common::Assignment::asstype_t asstype = t_ass->get_asstype();
    switch (asstype) {
    case Common::Assignment::A_TYPE:
      if (t_ass->get_Type()->get_typetype() != Common::Type::T_CLASS ||
          ref->get_reftype() != Common::Ref_simple::REF_THIS) {
        ref->error("Reference to a value, template, timer, port or class object "
          "was expected instead of %s", t_ass->get_description().c_str());
        return;
      }
      // else fall through
    case Common::Assignment::A_FUNCTION_RVAL:
    case Common::Assignment::A_FUNCTION_RTEMP:
    case Common::Assignment::A_EXT_FUNCTION_RVAL:
    case Common::Assignment::A_EXT_FUNCTION_RTEMP:
      if (asstype != Common::Assignment::A_TYPE) {
        ref->get_my_scope()->chk_runs_on_clause(t_ass, *this, "call");
      }
    case Common::Assignment::A_CONST:
    case Common::Assignment::A_EXT_CONST:
    case Common::Assignment::A_MODULEPAR:
    case Common::Assignment::A_MODULEPAR_TEMP:
    case Common::Assignment::A_TEMPLATE:
    case Common::Assignment::A_VAR:
    case Common::Assignment::A_EXCEPTION:
    case Common::Assignment::A_VAR_TEMPLATE:
    case Common::Assignment::A_PAR_VAL_IN:
    case Common::Assignment::A_PAR_VAL_OUT:
    case Common::Assignment::A_PAR_VAL_INOUT:
    case Common::Assignment::A_PAR_TEMPL_IN:
    case Common::Assignment::A_PAR_TEMPL_OUT:
    case Common::Assignment::A_PAR_TEMPL_INOUT: {
      // the reference points to a value or template-like entity
      // checking sub-references
      FieldOrArrayRefs *subrefs = ref->get_subrefs();
      if (subrefs && t_ass->get_Type()->get_field_type(subrefs,
	Type::EXPECTED_DYNAMIC_VALUE)) {
	// subrefs seems to be correct
	// also checking the presence of referred fields if possible
	if (asstype == Common::Assignment::A_CONST) {
	  ReferenceChain refch(ref, "While searching referenced value");
	  t_ass->get_Value()->get_refd_sub_value(subrefs, 0, false, &refch);
	} else if (asstype == Common::Assignment::A_TEMPLATE) {
	  ReferenceChain refch(ref, "While searching referenced template");
	  t_ass->get_Template()
            ->get_refd_sub_template(subrefs, false, &refch);
	}
      }
      break; }
    case Common::Assignment::A_TIMER:
    case Common::Assignment::A_PORT: {
      ArrayDimensions *t_dims = t_ass->get_Dimensions();
      if (t_dims) t_dims->chk_indices(ref, t_ass->get_assname(), true,
	Type::EXPECTED_DYNAMIC_VALUE);
      else if (ref->get_subrefs()) ref->error("Reference to single %s "
	"cannot have field or array sub-references",
	t_ass->get_description().c_str());
      break; }
    case Common::Assignment::A_PAR_TIMER:
    case Common::Assignment::A_PAR_PORT:
      if (ref->get_subrefs()) ref->error("Reference to %s cannot have "
	"field or array sub-references", t_ass->get_description().c_str());
      break;
    case Common::Assignment::A_FUNCTION:
    case Common::Assignment::A_EXT_FUNCTION:
      ref->error("Reference to a value, template, timer, port or class object "
	"was expected instead of a call of %s, which does not have return type",
	t_ass->get_description().c_str());
      break;
    default:
      ref->error("Reference to a value, template, timer, port or class object "
	"was expected instead of %s", t_ass->get_description().c_str());
    }
  }

  void LogArgument::chk_val()
  {
    // literal enumerated values cannot appear in this context
    val->set_lowerid_to_ref();
    switch (val->get_valuetype()) {
    case Value::V_CSTR: {
      string *t_cstr = new string(val->get_val_str());
      delete val;
      cstr = t_cstr;
      logargtype = L_STR;
      return; }
    case Value::V_REFD: {
      Reference *t_ref = val->steal_ttcn_ref();
      delete val;
      ref = t_ref;
      logargtype = L_REF;
      chk_ref();
      return; }
    case Value::V_EXPR:
      if (val->get_optype() == Value::OPTYPE_MATCH) logargtype = L_MATCH;
      else logargtype = L_VAL;
      break;
    case Value::V_MACRO:
      logargtype = L_MACRO;
      break;
    default:
      logargtype = L_VAL;
    } // switch
    Type *governor = val->get_expr_governor(Type::EXPECTED_DYNAMIC_VALUE);
    if (governor) {
      val->set_my_governor(governor);
      (void)governor->chk_this_value(val, 0, Type::EXPECTED_DYNAMIC_VALUE,
        INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK);
      if (logargtype == L_VAL && val->get_valuetype() == Value::V_CSTR
	&& !val->is_unfoldable()) { // string value known at compile time
	string *t_cstr = new string(val->get_val_str());
	delete val;
	cstr = t_cstr;
	logargtype = L_STR;
	return;
      }
      if (logargtype == L_MACRO) {
	switch (val->get_valuetype()) {
	case Value::V_CSTR: {
	  // the macro was evaluated to a charstring value
	  string *t_cstr = new string(val->get_val_str());
	  delete val;
	  cstr = t_cstr;
	  logargtype = L_STR;
	  break; }
	case Value::V_MACRO:
	  // the macro could not be evaluated at compile time
	  // leave logargtype as is
	  break;
	default:
	  // the macro was evaluated to other value (e.g. integer)
	  logargtype = L_VAL;
	}
      }
    } else {
      val->error("Cannot determine the type of the argument");
      delete val;
      logargtype = L_ERROR;
    }
  }

  void LogArgument::dump(unsigned int level) const
  {
    Node::dump(level++);
    switch (logargtype) {
    case L_ERROR:
      DEBUG(level, "*error*");
      break;
    case L_UNDEF:
      DEBUG(level, "*undef*");
      break;
    case L_TI:
      DEBUG(level, "TemplateInstance");
      break;
    case L_VAL:
      val->dump(level);
      break;
    case L_MATCH:
      DEBUG(level, "Match");
      break;
    case L_MACRO:
      DEBUG(level, "Macro");
      break;
    case L_REF:
      DEBUG(level, "Reference");
      break;
    case L_STR:
      DEBUG(level, "String=`%s'", cstr->c_str());
      break;
    default:
      FATAL_ERROR("LogArgument::~LogArgument()");
    } // switch
  }

  void LogArgument::set_code_section(
    GovernedSimple::code_section_t p_code_section)
  {
    switch (logargtype) {
    case L_UNDEF:
    case L_TI:
      ti->set_code_section(p_code_section);
      break;
    case L_VAL:
    case L_MATCH:
    case L_MACRO:
      val->set_code_section(p_code_section);
      break;
    case L_REF:
      ref->set_code_section(p_code_section);
      break;
    case L_STR:
    case L_ERROR:
      break;
    default:
      FATAL_ERROR("LogArgument::set_code_section()");
    } // switch
  }

  char *LogArgument::generate_code_log(char *str)
  {
    expression_struct expr;
    Code::init_expr(&expr);
    generate_code_expr(&expr);
    str = Code::merge_free_expr(str, &expr);
    return str;
  }

  void LogArgument::chk_recursions(ReferenceChain& refch)
  {
    switch (logargtype) {
    case L_UNDEF:
    case L_TI:
      ti->chk_recursions(refch);
      break;
    case L_VAL:
    case L_MATCH:
    case L_MACRO:
      val->chk_recursions(refch);
      break;
    case L_REF: {
      Common::Assignment *ass = ref->get_refd_assignment();
      if (!ass) break;
      refch.add(ass->get_fullname());
    } break;
    case L_STR:
    case L_ERROR:
      break;
    default:
      FATAL_ERROR("LogArgument::chk_recursions()");
    } // switch
  }

  bool LogArgument::has_single_expr()
  {
    switch (logargtype) {
    case L_UNDEF:
    case L_TI:
      return ti->has_single_expr();
    case L_VAL:
    case L_MATCH:
    case L_MACRO:
      return ( val->has_single_expr() && !val->explicit_cast_needed() );
    case L_REF: {
      Common::Assignment *ass = ref->get_refd_assignment();
      switch (ass->get_asstype()) {
      case Common::Assignment::A_CONST:
      case Common::Assignment::A_EXT_CONST:
        return ref->has_single_expr();
      default:
        return false;
      }
      break; }
    case L_STR:
    case L_ERROR:
      return true;
    default:
      FATAL_ERROR("LogArgument::has_single_expr()");
    } // switch
    return true;
  }

  void LogArgument::generate_code_expr(expression_struct *expr)
  {
    switch(logargtype) {
    case L_TI: {
      if (ti->is_only_specific_value()) {
        // use the embedded specific value for code generation
        ti->get_Template()->get_specific_value()->generate_code_log(expr);
      } else {
	ti->generate_code(expr);
	expr->expr = mputstr(expr->expr, ".log()");
      }
      break; }
    case L_VAL:
      val->generate_code_log(expr);
      break;
    case L_MATCH:
      val->generate_code_log_match(expr);
      break;
    case L_MACRO:
      if (val->has_single_expr()) {
	expr->expr = mputprintf(expr->expr, "TTCN_Logger::log_event_str(%s)",
	  val->get_single_expr().c_str());
      } else val->generate_code_log(expr);
      break;
    case L_REF: {
      ref->generate_code_const_ref(expr);
      expr->expr=mputstr(expr->expr, ".log()");
      break;}
    case L_STR: {
      size_t str_len = cstr->size();
      const char *str_ptr = cstr->c_str();
      switch (str_len) {
      case 0:
	// the string is empty: do not generate any code
      case 1:
	// the string has one character: use log_char member
	expr->expr = mputstr(expr->expr, "TTCN_Logger::log_char('");
	expr->expr = Code::translate_character(expr->expr, *str_ptr, false);
	expr->expr = mputstr(expr->expr, "')");
	break;
      default:
	// the string has more characters: use log_event_str member
	expr->expr = mputstr(expr->expr, "TTCN_Logger::log_event_str(\"");
	expr->expr = Code::translate_string(expr->expr, str_ptr);
	expr->expr = mputstr(expr->expr, "\")");
      }
      break; }
    default:
      FATAL_ERROR("LogArgument::generate_code_expr()");
    } // switch
  }

  // =================================
  // ===== LogArguments
  // =================================

  LogArguments::LogArguments(const LogArguments& p)
  {
    for (size_t i = 0; i < p.logargs.size(); ++i) {
      logargs.add(p.logargs[i]->clone());
    }
  }
  
  LogArguments::~LogArguments()
  {
    for(size_t i=0; i<logargs.size(); i++) delete logargs[i];
    logargs.clear();
  }

  LogArguments *LogArguments::clone() const
  {
    return new LogArguments(*this);
  }

  void LogArguments::add_logarg(LogArgument *p_logarg)
  {
    if(!p_logarg)
      FATAL_ERROR("LogArguments::add_logarg()");
    logargs.add(p_logarg);
  }

  void LogArguments::set_my_scope(Scope *p_scope)
  {
    for(size_t i=0; i<logargs.size(); i++)
      logargs[i]->set_my_scope(p_scope);
  }

  void LogArguments::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for(size_t i=0; i<logargs.size(); i++)
      logargs[i]->set_fullname(p_fullname+".logargs_"+Int2string(i+1));
  }

  void LogArguments::chk()
  {
    for(size_t i=0; i<logargs.size(); i++)
      logargs[i]->chk();
  }

  void LogArguments::join_strings()
  {
    // points to the previous string argument otherwise it is NULL
    LogArgument *prev_arg = 0;
    for (size_t i = 0; i < logargs.size(); ) {
      LogArgument *arg = logargs[i];
      if (arg->get_type() == LogArgument::L_STR) {
	const string& str = arg->get_str();
	if (str.size() > 0) {
	  // the current argument is a non-empty string
	  if (prev_arg) {
	    // append str to prev_arg and drop arg
	    prev_arg->append_str(str);
	    delete arg;
	    logargs.replace(i, 1);
	    // don't increment i
	  } else {
	    // keep it for the next iteration
	    prev_arg = arg;
	    i++;
	  }
	} else {
	  // the current argument is an empty string
	  // simply drop it unless it is the only argument
	  // note: we must distinguish between log() and log("")
	  if (i > 0 || logargs.size() > 1) {
	    delete arg;
	    logargs.replace(i, 1);
	    // don't increment i
	  } else break;
	}
      } else {
	// the current argument is not a string
	// forget the previous arg
	prev_arg = 0;
	i++;
      }
    }
  }

  void LogArguments::set_code_section(
    GovernedSimple::code_section_t p_code_section)
  {
    for (size_t i = 0; i < logargs.size(); i++)
      logargs[i]->set_code_section(p_code_section);
  }

  char *LogArguments::generate_code(char *str)
  {
    for(size_t i=0; i<logargs.size(); i++)
      str=logargs[i]->generate_code_log(str);
    return str;
  }

  void LogArguments::chk_recursions(ReferenceChain& refch)
  {
    for (size_t i=0; i<logargs.size(); i++) {
      refch.mark_state();
      logargs[i]->chk_recursions(refch);
      refch.prev_state();
    }
  }

  bool LogArguments::has_single_expr()
  {
    bool i_have_single_expr = true;
    for (size_t i=0; i<logargs.size(); i++)
      i_have_single_expr = i_have_single_expr && logargs[i]->has_single_expr();
    return i_have_single_expr;
  }

  void LogArguments::generate_code_expr(expression_struct *expr)
  {
    expr->expr = mputstr(expr->expr, "(TTCN_Logger::begin_event_log2str(),");
    for(size_t i=0; i<logargs.size(); i++) {
      logargs[i]->generate_code_expr(expr);
      expr->expr = mputc(expr->expr, ','); // comma operator
    }
    expr->expr = mputstr(expr->expr, "TTCN_Logger::end_event_log2str())");
  }

  // =================================
  // ===== IfClause
  // =================================

  IfClause::IfClause(Value *p_expr, StatementBlock *p_block)
    : expr(p_expr), block(p_block)
  {
    if(!expr || !block)
      FATAL_ERROR("IfClause::IfClause()");
  }

  IfClause::~IfClause()
  {
    delete expr;
    delete block;
  }

  IfClause *IfClause::clone() const
  {
    FATAL_ERROR("IfClause::clone");
  }

  void IfClause::set_my_scope(Scope *p_scope)
  {
    expr->set_my_scope(p_scope);
    block->set_my_scope(p_scope);
  }

  void IfClause::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    expr->set_fullname(p_fullname+".expr");
    block->set_fullname(p_fullname+".block");
  }

  bool IfClause::has_receiving_stmt() const
  {
    return block->has_receiving_stmt();
  }

  void IfClause::chk(bool& unreach)
  {
    Error_Context cntxt(this, "In if statement");
    if(unreach) warning("Control never reaches this code because of"
                        " previous effective condition(s)");
    expr->chk_expr_bool(Type::EXPECTED_DYNAMIC_VALUE);
    if(!expr->is_unfoldable()) {
      if(expr->get_val_bool()) unreach=true;
      else block->warning("Control never reaches this code because the"
                         " conditional expression evaluates to false");
    }
    block->chk();
  }

  void IfClause::set_code_section(
    GovernedSimple::code_section_t p_code_section)
  {
    expr->set_code_section(p_code_section);
    block->set_code_section(p_code_section);
  }

  char* IfClause::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars,
                                size_t& blockcount, bool& unreach, bool& eachfalse)
  {
    if(unreach) return str;
    if(!expr->is_unfoldable()) {
      if(expr->get_val_bool()) unreach=true;
      else return str;
    }
    if (!eachfalse) str = mputstr(str, "else ");
    if (!unreach) {
      if (!eachfalse) {
	str = mputstr(str, "{\n");
	blockcount++;
      }
      str = expr->update_location_object(str);
      str = expr->generate_code_tmp(str, "if (", blockcount);
      str = mputstr(str, ") ");
    }
    eachfalse = false;
    str=mputstr(str, "{\n");
    if (debugger_active) {
      str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
    }
    str=block->generate_code(str, def_glob_vars, src_glob_vars);
    str=mputstr(str, "}\n");
    return str;
  }

  void IfClause::ilt_generate_code(ILT *ilt, const char *end_label,
                                   bool& unreach)
  {
    if(unreach) return;
    if(!expr->is_unfoldable()) {
      if(expr->get_val_bool()) unreach=true;
      else return;
    }
    char*& str=ilt->get_out_branches();
    char *label=0;
    if(!unreach) {
      size_t blockcount=0;
      label=mprintf("%s_l%lu",
                    ilt->get_my_tmpid().c_str(),
                    static_cast<unsigned long>( ilt->get_new_label_num() ));
      str=expr->update_location_object(str);
      str=expr->generate_code_tmp(str, "if(!", blockcount);
      str=mputprintf(str, ") goto %s;\n", label);
      while(blockcount-->0) str=mputstr(str, "}\n");
    }
    block->ilt_generate_code(ilt);
    if(!unreach) {
      str=mputprintf(str, "goto %s;\n%s:\n",
                     end_label, label);
      Free(label);
    }
  }

  void IfClause::set_parent_path(WithAttribPath* p_path) {
    block->set_parent_path(p_path);
  }

  void IfClause::dump(unsigned int level) const {
    DEBUG(level, "If clause!");
    expr->dump(level + 1);
    block->dump(level + 1);
  }

  // =================================
  // ===== IfClauses
  // =================================

  IfClauses::~IfClauses()
  {
    for(size_t i=0; i<ics.size(); i++) delete ics[i];
    ics.clear();
  }

  IfClauses *IfClauses::clone() const
  {
    FATAL_ERROR("IfClauses::clone");
  }

  void IfClauses::add_ic(IfClause *p_ic)
  {
    if(!p_ic)
      FATAL_ERROR("IfClauses::add_ic()");
    ics.add(p_ic);
  }

  void IfClauses::add_front_ic(IfClause *p_ic)
  {
    if(!p_ic)
      FATAL_ERROR("IfClauses::add_front_ic()");
    ics.add_front(p_ic);
  }

  void IfClauses::set_my_scope(Scope *p_scope)
  {
    for(size_t i=0; i<ics.size(); i++)
      ics[i]->set_my_scope(p_scope);
  }

  void IfClauses::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for(size_t i=0; i<ics.size(); i++)
      ics[i]->set_fullname(p_fullname+".ic_"+Int2string(i+1));
  }

  void IfClauses::set_my_sb(StatementBlock *p_sb, size_t p_index)
  {
    for(size_t i=0; i<ics.size(); i++)
      ics[i]->get_block()->set_my_sb(p_sb, p_index);
  }

  void IfClauses::set_my_def(Definition *p_def)
  {
    for(size_t i=0; i<ics.size(); i++)
      ics[i]->get_block()->set_my_def(p_def);
  }

  void IfClauses::set_my_ags(AltGuards *p_ags)
  {
    for(size_t i=0; i<ics.size(); i++)
      ics[i]->get_block()->set_my_ags(p_ags);
  }

  void IfClauses::set_my_laic_stmt(AltGuards *p_ags, Statement *p_loop_stmt)
  {
    for(size_t i=0; i<ics.size(); i++)
      ics[i]->get_block()->set_my_laic_stmt(p_ags, p_loop_stmt);
  }

  StatementBlock::returnstatus_t IfClauses::has_return
    (StatementBlock *elseblock) const
  {
    StatementBlock::returnstatus_t ret_val = StatementBlock::RS_MAYBE;
    for (size_t i = 0; i < ics.size(); i++) {
      switch (ics[i]->get_block()->has_return()) {
      case StatementBlock::RS_NO:
	if (ret_val == StatementBlock::RS_YES) return StatementBlock::RS_MAYBE;
	else ret_val = StatementBlock::RS_NO;
	break;
      case StatementBlock::RS_YES:
	if (ret_val == StatementBlock::RS_NO) return StatementBlock::RS_MAYBE;
	else ret_val = StatementBlock::RS_YES;
	break;
      default:
	return StatementBlock::RS_MAYBE;
      }
    }
    StatementBlock::returnstatus_t else_status;
    if (elseblock) else_status = elseblock->has_return();
    else else_status = StatementBlock::RS_NO;
    switch (else_status) {
    case StatementBlock::RS_NO:
      if (ret_val == StatementBlock::RS_YES) return StatementBlock::RS_MAYBE;
      else ret_val = StatementBlock::RS_NO;
      break;
    case StatementBlock::RS_YES:
      if (ret_val == StatementBlock::RS_NO) return StatementBlock::RS_MAYBE;
      else ret_val = StatementBlock::RS_YES;
      break;
    default:
      return StatementBlock::RS_MAYBE;
    }
    return ret_val;
  }

  bool IfClauses::has_receiving_stmt() const
  {
    for(size_t i=0; i<ics.size(); i++)
      if(ics[i]->has_receiving_stmt()) return true;
    return false;
  }

  void IfClauses::chk(bool& unreach)
  {
    for(size_t i=0; i<ics.size(); i++)
      ics[i]->chk(unreach);
  }

  void IfClauses::chk_allowed_interleave()
  {
    for (size_t i = 0; i < ics.size(); i++)
      ics[i]->get_block()->chk_allowed_interleave();
  }

  void IfClauses::set_code_section(
    GovernedSimple::code_section_t p_code_section)
  {
    for (size_t i = 0; i < ics.size(); i++)
      ics[i]->set_code_section(p_code_section);
  }

  char* IfClauses::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars,
                                 size_t& blockcount, bool& unreach, bool& eachfalse)
  {
    for(size_t i=0; i<ics.size(); i++) {
      if(unreach) return str;
      str=ics[i]->generate_code(str, def_glob_vars, src_glob_vars, blockcount, unreach, eachfalse);
    }
    return str;
  }

  void IfClauses::ilt_generate_code(ILT *ilt, const char *end_label,
                                    bool& unreach)
  {
    for(size_t i=0; i<ics.size(); i++) {
      if(unreach) return;
      ics[i]->ilt_generate_code(ilt, end_label, unreach);
    }
  }

  void IfClauses::set_parent_path(WithAttribPath* p_path) {
    for (size_t i = 0; i < ics.size(); i++)
      ics[i]->set_parent_path(p_path);
  }

  void IfClauses::dump(unsigned int level) const {
    DEBUG(level, "%lu if clauses", static_cast<unsigned long>(ics.size()));
    for (size_t i = 0; i < ics.size(); i++)
      ics[i]->dump(level + 1);
  }

  // =================================
  // ===== SelectCase
  // =================================

  SelectCase::SelectCase(TemplateInstances *p_tis, StatementBlock *p_block)
    : tis(p_tis), block(p_block)
  {
    if(!block)
      FATAL_ERROR("SelectCase::SelectCase()");
  }

  SelectCase::~SelectCase()
  {
    delete tis;
    delete block;
  }

  SelectCase *SelectCase::clone() const
  {
    FATAL_ERROR("SelectCase::clone");
  }

  void SelectCase::set_my_scope(Scope *p_scope)
  {
    if(tis) tis->set_my_scope(p_scope);
    block->set_my_scope(p_scope);
  }

  void SelectCase::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    if(tis) tis->set_fullname(p_fullname+".tis");
    block->set_fullname(p_fullname+".block");
  }

  /** \todo review */
  void SelectCase::chk(Type *p_gov, bool& unreach)
  {
    Error_Context cntxt(this, "In select case statement");
    if(unreach) warning("Control never reaches this code because of"
                        " previous effective case(s)");
    if(tis)
      for(size_t i=0; i<tis->get_nof_tis(); i++)
        tis->get_ti_byIndex(i)->chk(p_gov);
    else unreach=true; // else statement
    block->chk();
  }

  void SelectCase::set_code_section
  (GovernedSimple::code_section_t p_code_section)
  {
    if(tis) tis->set_code_section(p_code_section);
    block->set_code_section(p_code_section);
  }

  /** \todo review */
  char* SelectCase::generate_code_if(char *str, const char *tmp_prefix,
                                     const char *expr_name, size_t idx,
                                     bool& unreach)
  {
    if(unreach) return str;
    if(tis) {
      for(size_t i=0; i<tis->get_nof_tis(); i++) {
        TemplateInstance *ti=tis->get_ti_byIndex(i);
        Template *tb=ti->get_Template();
        bool is_value = NULL == ti->get_DerivedRef() && tb->is_Value();
        expression_struct exprs;
        Code::init_expr(&exprs);
        if (is_value) {
          if (tb->get_templatetype() == Template::SPECIFIC_VALUE) {
            tb->get_specific_value()->generate_code_expr_mandatory(&exprs);
          }
          else {
            Value* val = tb->get_Value();
            if (NULL == val->get_my_governor()) {
              // the value's governor could not be determined, treat it as a non-value template
              is_value = false;
            }
            else {
              val->generate_code_expr_mandatory(&exprs);
            }
            delete val;
          }
        }
        if (!is_value) {
          ti->generate_code(&exprs);
        }
        str=tb->update_location_object(str);
        if(!exprs.preamble && !exprs.postamble) {
          str=mputstr(str, "if(");
          if(!is_value)
            str=mputprintf(str, "%s.match(%s%s)", exprs.expr, expr_name,
              omit_in_value_list ? ", TRUE" : "");
          else str=mputprintf(str, "%s == %s", expr_name, exprs.expr);
          str=mputprintf(str, ") goto %s_%lu;\n", tmp_prefix,
            static_cast<unsigned long>( idx ));
          Code::free_expr(&exprs);
        }
        else {
          str=mputprintf(str, "{\nboolean %s_%lub;\n", tmp_prefix,
            static_cast<unsigned long>( idx ));
          char *s=exprs.expr;
          exprs.expr=mprintf("%s_%lub = ", tmp_prefix, static_cast<unsigned long>( idx ));
          if(!is_value)
            exprs.expr=mputprintf(exprs.expr, "%s.match(%s%s)", s, expr_name,
              omit_in_value_list ? ", TRUE" : "");
          else exprs.expr=mputprintf(exprs.expr, "(%s == %s)", expr_name, s);
          Free(s);
          str=Code::merge_free_expr(str, &exprs);
          str=mputprintf(str, "if(%s_%lub) goto %s_%lu;\n}\n",
            tmp_prefix, static_cast<unsigned long>( idx ), tmp_prefix, static_cast<unsigned long>( idx ));
        }
      } // for i
    } // if tis
    else {
      unreach=true; // else statement
      str=mputprintf(str, "goto %s_%lu;\n", tmp_prefix, static_cast<unsigned long>( idx ));
    }
    return str;
  }
  
  char* SelectCase::generate_code_case(char *str, char*& def_glob_vars, char*& src_glob_vars,
                                       bool &else_branch,
                                       vector<const Int>& used_numbers) {
    bool already_present_all = true; // to decide if we need to generate the block
    if (tis != NULL) {
      for (size_t i = 0; i < tis->get_nof_tis(); i++) {
        int_val_t* val = tis->get_ti_byIndex(i)->get_specific_value()->get_val_Int();
        bool already_present = false; // to decide if we need to generate the case
        for (size_t j = 0; j < used_numbers.size(); j++) {
          if (*(used_numbers[j]) == val->get_val()) {
            already_present = true;
            break;
          }
        }
        used_numbers.add(&val->get_val());
        if (!already_present) {
          already_present_all = false;
          str = mputprintf(str, "case(%lld):\n", 
          tis->get_ti_byIndex(i)->get_specific_value()->get_val_Int()->get_val());
        }
      }
    } else {
      else_branch = true;
      already_present_all = false;
      str = mputstr(str, "default:\n"); // The else branch
    }
    if (!already_present_all) {
      str = mputstr(str, "{\n");
      str = block->generate_code(str, def_glob_vars, src_glob_vars);
      str = mputstr(str, "break;\n}\n");
    }
    return str;
  }

  /** \todo review */
  char* SelectCase::generate_code_stmt(char *str, char*& def_glob_vars, char*& src_glob_vars,
                                       const char *tmp_prefix,
                                       size_t idx, bool& unreach)
  {
    if(unreach) return str;
    if(!tis) unreach=true;
    str=mputprintf(str, "%s_%lu:\n{\n", tmp_prefix, static_cast<unsigned long>( idx ));
    if (debugger_active) {
      str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
    }
    str=block->generate_code(str, def_glob_vars, src_glob_vars);
    if(block->has_return() != StatementBlock::RS_YES) {
      str=mputprintf(str, "goto %s_end;\n}\n", tmp_prefix);
    } else {
      str = mputstr(str, "}\n");
    }
    return str;
  }

  void SelectCase::ilt_generate_code_stmt(ILT *ilt, const char *tmp_prefix,
                                          size_t idx, bool& unreach)
  {
    if(unreach) return;
    if(!tis) unreach=true;
    char*& str=ilt->get_out_branches();
    str=mputprintf(str, "%s_%lu:\n", tmp_prefix, static_cast<unsigned long>( idx ));
    bool has_recv=block->has_receiving_stmt();
    if(!has_recv) {
      str=mputstr(str, "{\n");
      str=block->generate_code(str, ilt->get_out_def_glob_vars(),
        ilt->get_out_src_glob_vars());
    }
    else block->ilt_generate_code(ilt);
    str=mputprintf(str, "goto %s_end;\n", tmp_prefix);
    if(!has_recv)
      str=mputstr(str, "}\n");
  }

  void SelectCase::set_parent_path(WithAttribPath* p_path) {
    block->set_parent_path(p_path);
  }

  // =================================
  // ===== SelectCases
  // =================================

  SelectCases::~SelectCases()
  {
    for(size_t i=0; i<scs.size(); i++) delete scs[i];
    scs.clear();
  }

  SelectCases *SelectCases::clone() const
  {
    FATAL_ERROR("SelectCases::clone");
  }

  void SelectCases::add_sc(SelectCase *p_sc)
  {
    if(!p_sc)
      FATAL_ERROR("SelectCases::add_sc()");
    scs.add(p_sc);
  }

  void SelectCases::set_my_scope(Scope *p_scope)
  {
    for(size_t i=0; i<scs.size(); i++)
      scs[i]->set_my_scope(p_scope);
  }

  void SelectCases::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for(size_t i=0; i<scs.size(); i++)
      scs[i]->set_fullname(p_fullname+".sc_"+Int2string(i+1));
  }

  void SelectCases::set_my_sb(StatementBlock *p_sb, size_t p_index)
  {
    for(size_t i=0; i<scs.size(); i++)
      scs[i]->get_block()->set_my_sb(p_sb, p_index);
  }

  void SelectCases::set_my_def(Definition *p_def)
  {
    for(size_t i=0; i<scs.size(); i++)
      scs[i]->get_block()->set_my_def(p_def);
  }

  void SelectCases::set_my_ags(AltGuards *p_ags)
  {
    for(size_t i=0; i<scs.size(); i++)
      scs[i]->get_block()->set_my_ags(p_ags);
  }

  void SelectCases::set_my_laic_stmt(AltGuards *p_ags, Statement *p_loop_stmt)
  {
    for(size_t i=0; i<scs.size(); i++)
      scs[i]->get_block()->set_my_laic_stmt(p_ags, p_loop_stmt);
  }

  StatementBlock::returnstatus_t SelectCases::has_return() const
  {
    StatementBlock::returnstatus_t ret_val = StatementBlock::RS_MAYBE;
    bool has_else = false;
    for (size_t i = 0; i < scs.size(); i++) {
      SelectCase *sc = scs[i];
      switch (sc->get_block()->has_return()) {
      case StatementBlock::RS_NO:
	if (ret_val == StatementBlock::RS_YES) return StatementBlock::RS_MAYBE;
	else ret_val = StatementBlock::RS_NO;
	break;
      case StatementBlock::RS_YES:
	if (ret_val == StatementBlock::RS_NO) return StatementBlock::RS_MAYBE;
	else ret_val = StatementBlock::RS_YES;
	break;
      default:
	return StatementBlock::RS_MAYBE;
      }
      if (!sc->get_tis()) {
	has_else = true;
	break;
      }
    }
    if (!has_else && ret_val == StatementBlock::RS_YES)
      return StatementBlock::RS_MAYBE;
    else return ret_val;
  }

  bool SelectCases::has_receiving_stmt() const
  {
    for(size_t i=0; i<scs.size(); i++)
      if(scs[i]->get_block()->has_receiving_stmt()) return true;
    return false;
  }
  
  bool SelectCases::can_generate_switch() const {
    for(size_t i=0; i<scs.size(); i++) {
      TemplateInstances* tis = scs[i]->get_tis();
      if (tis == NULL) { // the else brach
        continue;
      }
      for(size_t j=0; j<tis->get_nof_tis(); j++) {
        Value * v = tis->get_ti_byIndex(j)->get_specific_value();
        if (v == NULL) return false;
        if (v->is_unfoldable()) return false;
        if (!v->get_val_Int()->is_native()) return false;
      }
    }
    return true;
  }

  /** \todo review */
  void SelectCases::chk(Type *p_gov)
  {
    bool unreach=false;
    for(size_t i=0; i<scs.size(); i++)
      scs[i]->chk(p_gov, unreach);
  }

  void SelectCases::chk_allowed_interleave()
  {
    for (size_t i = 0; i < scs.size(); i++)
      scs[i]->get_block()->chk_allowed_interleave();
  }

  void SelectCases::set_code_section
  (GovernedSimple::code_section_t p_code_section)
  {
    for(size_t i=0; i<scs.size(); i++)
      scs[i]->set_code_section(p_code_section);
  }

  char* SelectCases::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars,
                                   const char *tmp_prefix, const char *expr_name)
  {
    bool unreach=false;
    for(size_t i=0; i<scs.size(); i++) {
      str=scs[i]->generate_code_if(str, tmp_prefix, expr_name, i, unreach);
      if(unreach) break;
    }
    if(!unreach) str=mputprintf(str, "goto %s_end;\n", tmp_prefix);
    unreach=false;
    for(size_t i=0; i<scs.size(); i++) {
      str=scs[i]->generate_code_stmt(str, def_glob_vars, src_glob_vars, tmp_prefix, i, unreach);
      if(unreach) break;
    }
    if(!unreach || has_return() != StatementBlock::RS_YES) {
      str=mputprintf(str, "%s_end: /* empty */;\n", tmp_prefix);
    }
    return str;
  }
  
  char* SelectCases::generate_code_switch(char *str, char*& def_glob_vars, char*& src_glob_vars,
                                          const char *expr_name)
  {
    bool else_branch=false;
    vector<const Int> used_numbers; // store the case values to remove duplicates
    str=mputprintf(str, "switch(%s.get_long_long_val()) {\n", expr_name);
    for(size_t i=0; i<scs.size(); i++) {
      str=scs[i]->generate_code_case(str, def_glob_vars, src_glob_vars, else_branch, used_numbers);
      if(else_branch) break;
    }
    str=mputprintf(str, "};");
    used_numbers.clear();
    return str;
  }

  void SelectCases::ilt_generate_code(ILT *ilt, const char *tmp_prefix,
                                      const char *expr_init,
                                      const char *head_expr,
                                      const char *expr_name)
  {
    char*& str=ilt->get_out_branches();
    if(strlen(expr_init)) {
      str=mputstr(str, "{\n"); // (1)
      str=mputstr(str, expr_init);
    }
    str=mputstr(str, head_expr);
    bool unreach=false;
    for(size_t i=0; i<scs.size(); i++) {
      if(unreach) break;
      str=scs[i]->generate_code_if(str, tmp_prefix, expr_name, i, unreach);
    }
    if(!unreach) str=mputprintf(str, "goto %s_end;\n", tmp_prefix);
    if(strlen(expr_init)) str=mputstr(str, "}\n"); // (1)
    unreach=false;
    for(size_t i=0; i<scs.size(); i++) {
      if(unreach) break;
      scs[i]->ilt_generate_code_stmt(ilt, tmp_prefix, i, unreach);
    }
    str=mputprintf(str, "%s_end:\n", tmp_prefix);
  }
  
  void SelectCases::ilt_generate_code_switch(ILT *ilt, const char *expr_init, const char *head_expr, const char *expr_name)
  {
    char*& str=ilt->get_out_branches();
    if(expr_init[0]) {
        str=mputstr(str, "{\n");
        str=mputstr(str, expr_init);
    }
    str=mputstr(str, head_expr);
    bool else_branch=false;
    vector<const Int> used_numbers; // store the case values to remove duplicates
    str=mputprintf(str, "switch(%s.get_long_long_val()) {\n", expr_name);
    for(size_t i=0; i<scs.size(); i++) {
      str=scs[i]->generate_code_case(str, ilt->get_out_def_glob_vars(),
        ilt->get_out_src_glob_vars(), else_branch, used_numbers);
      if(else_branch) break;
    }
    str=mputprintf(str, "};");
    used_numbers.clear();
    if (expr_init[0]) str=mputstr(str, "}\n");
  }

  void SelectCases::set_parent_path(WithAttribPath* p_path) {
    for (size_t i = 0; i < scs.size(); i++)
      scs[i]->set_parent_path(p_path);
  }
  
  // =================================
  // ===== SelectUnion
  // =================================
  
  SelectUnion::SelectUnion(StatementBlock *p_block)
    : block(p_block)
  {
    if(!block)
      FATAL_ERROR("SelectUnion::SelectUnion()");
  }

  SelectUnion::~SelectUnion()
  {
    for(size_t i = 0; i < ids.size(); i++) {
      delete ids[i];
    }
    ids.clear();
    delete block;
  }

  SelectUnion *SelectUnion::clone() const
  {
    FATAL_ERROR("SelectUnion::clone");
  }

  void SelectUnion::set_my_scope(Scope *p_scope)
  {
    block->set_my_scope(p_scope);
  }

  void SelectUnion::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    block->set_fullname(p_fullname+".block");
  }
  
  void SelectUnion::add_id(Identifier *id) {
    if (!id) {
      FATAL_ERROR("SelectUnion::add_id()");
    }
    ids.add(id);
  }

  void SelectUnion::chk()
  {
    Error_Context cntxt(this, "In select union statement");
    block->chk();
  }

  void SelectUnion::set_code_section(GovernedSimple::code_section_t p_code_section)
  {
    block->set_code_section(p_code_section);
  }

  char* SelectUnion::generate_code_case(char *str, char*& def_glob_vars, char*& src_glob_vars,
                                        const char *type_name, bool &else_branch)
  {
    if (ids.size() != 0) {
      for (size_t i = 0; i < ids.size(); i++) {
        str = mputprintf(str, "case(%s::ALT_%s):\n",
          type_name, ids[i]->get_name().c_str());
      }
    } else {
      else_branch = true;
      str = mputstr(str, "default:\n"); // The else branch
    }
    
    str = mputstr(str, "{\n");
    str = block->generate_code(str, def_glob_vars, src_glob_vars);
    str = mputstr(str, "break;\n}\n");
    return str;
  }

  void SelectUnion::set_parent_path(WithAttribPath* p_path) {
    block->set_parent_path(p_path);
  }
  
  // =================================
  // ===== SelectUnions
  // =================================

  SelectUnions::~SelectUnions()
  {
    for(size_t i=0; i<sus.size(); i++) {
      delete sus[i];
    }
    sus.clear();
  }

  SelectUnions *SelectUnions::clone() const
  {
    FATAL_ERROR("SelectUnions::clone");
  }

  void SelectUnions::add_su(SelectUnion *p_su)
  {
    if(!p_su) {
      FATAL_ERROR("SelectUnions::add_su()");
    }
    sus.add(p_su);
  }

  void SelectUnions::set_my_scope(Scope *p_scope)
  {
    for(size_t i=0; i<sus.size(); i++) {
      sus[i]->set_my_scope(p_scope);
    }
  }

  void SelectUnions::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for(size_t i = 0; i < sus.size(); i++) {
      sus[i]->set_fullname(p_fullname+".su_"+Int2string(i+1));
    }
  }

  void SelectUnions::set_my_sb(StatementBlock *p_sb, size_t p_index)
  {
    for(size_t i=0; i<sus.size(); i++) {
      sus[i]->get_block()->set_my_sb(p_sb, p_index);
    }
  }

  void SelectUnions::set_my_def(Definition *p_def)
  {
    for(size_t i=0; i<sus.size(); i++) {
      sus[i]->get_block()->set_my_def(p_def);
    }
  }

  void SelectUnions::set_my_ags(AltGuards *p_ags)
  {
    for(size_t i=0; i<sus.size(); i++) {
      sus[i]->get_block()->set_my_ags(p_ags);
    }
  }

  void SelectUnions::set_my_laic_stmt(AltGuards *p_ags, Statement *p_loop_stmt)
  {
    for(size_t i=0; i<sus.size(); i++) {
      sus[i]->get_block()->set_my_laic_stmt(p_ags, p_loop_stmt);
    }
  }

  StatementBlock::returnstatus_t SelectUnions::has_return() const
  {
    StatementBlock::returnstatus_t ret_val = StatementBlock::RS_MAYBE;
    bool has_else = false;
    for (size_t i = 0; i < sus.size(); i++) {
      SelectUnion *su = sus[i];
      switch (su->get_block()->has_return()) {
      case StatementBlock::RS_NO:
        if (ret_val == StatementBlock::RS_YES) return StatementBlock::RS_MAYBE;
        else ret_val = StatementBlock::RS_NO;
        break;
      case StatementBlock::RS_YES:
        if (ret_val == StatementBlock::RS_NO) return StatementBlock::RS_MAYBE;
        else ret_val = StatementBlock::RS_YES;
        break;
      default:
        return StatementBlock::RS_MAYBE;
      }
      if (su->get_ids().empty()) { // The else branch
        has_else = true;
        break;
      }
    }
    if (!has_else && ret_val == StatementBlock::RS_YES)
      return StatementBlock::RS_MAYBE;
    else return ret_val;
  }

  bool SelectUnions::has_receiving_stmt() const
  {
    for(size_t i=0; i<sus.size(); i++) {
      if(sus[i]->get_block()->has_receiving_stmt()) {
        return true;
      }
    }
    return false;
  }

  void SelectUnions::chk()
  {
    for(size_t i = 0; i < sus.size(); i++) {
      sus[i]->chk();
    }
  }

  void SelectUnions::chk_allowed_interleave()
  {
    for (size_t i = 0; i < sus.size(); i++) {
      sus[i]->get_block()->chk_allowed_interleave();
    }
  }

  void SelectUnions::set_code_section(GovernedSimple::code_section_t p_code_section)
  {
    for(size_t i=0; i<sus.size(); i++) {
      sus[i]->set_code_section(p_code_section);
    }
  }

  char* SelectUnions::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars,
                                    const char *type_name, const char *loc)
  {
    str = mputprintf(str, "case(%s::UNBOUND_VALUE):\n", type_name);
    str = mputprintf(str, "%s", loc);
    str = mputstr(str, "TTCN_error(\"The union in the head shall be initialized\");\n");
    str = mputstr(str, "break;\n");
    bool else_branch = false;
    for (size_t i = 0; i < sus.size(); i++) {
      str = sus[i]->generate_code_case(str, def_glob_vars, src_glob_vars, type_name, else_branch);
    }
    if (!else_branch) {
      str = mputstr(str, "default:\nbreak;\n");
    }
    return str;
  }

  void SelectUnions::set_parent_path(WithAttribPath* p_path) {
    for (size_t i = 0; i < sus.size(); i++) {
      sus[i]->set_parent_path(p_path);
    }
  }
  
  // =================================
  // ===== SelectClassCase
  // =================================

  SelectClassCase::SelectClassCase(Common::Type* p_type, StatementBlock* p_block)
    : type(p_type), block(p_block), my_scope(NULL), always_false(false)
  {
    if (block == NULL) {
      FATAL_ERROR("SelectClassCase::SelectClassCase()");
    }
  }

  SelectClassCase::~SelectClassCase()
  {
    delete type;
    delete block;
  }

  SelectClassCase* SelectClassCase::clone() const
  {
    FATAL_ERROR("SelectClassCase::clone");
  }

  void SelectClassCase::set_my_scope(Scope* p_scope)
  {
    my_scope = p_scope;
    if (type != NULL) {
      type->set_my_scope(p_scope);
    }
    block->set_my_scope(p_scope);
  }

  void SelectClassCase::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    if (type != NULL) {
      type->set_fullname(p_fullname + ".type");
    }
    block->set_fullname(p_fullname + ".block");
  }

  void SelectClassCase::chk(ClassTypeBody* p_ref_class, bool& unreach)
  {
    Error_Context cntxt(this, "In select class case statement");
    if (unreach) {
      warning("Control never reaches this code because of previous "
        "effective case(s)");
    }
    if (type != NULL) {
      type->chk();
      Common::Type* type_last = type->get_type_refd_last();
      if (type_last->get_typetype() != Type::T_CLASS) {
        type->error("Class type was expected");
      }
      else if (!unreach) {
        ClassTypeBody* class_ = type_last->get_class_type_body();
        if (p_ref_class != NULL && !class_->is_parent_class(p_ref_class) &&
            !p_ref_class->is_parent_class(class_)) {
          block->warning("Control never reaches this code because the case will "
            "never be chosen");
          always_false = true;
        }
      }
    }
    else {
      unreach = true; // else statement
    }
    block->chk();
  }

  void SelectClassCase::set_code_section(GovernedSimple::code_section_t p_code_section)
  {
    block->set_code_section(p_code_section);
  }

  char* SelectClassCase::generate_code_if(char* str, const char* tmp_prefix,
                                          const char* ref_str, size_t idx,
                                          bool& unreach)
  {
    if (unreach || always_false) {
      return str;
    }
    if (type != NULL) {
      ClassTypeBody* class_ = type->get_type_refd_last()->get_class_type_body();
      str = mputprintf(str,
        "if (%s != NULL_VALUE && dynamic_cast<%s*>(*%s) != NULL) goto %s_%lu;\n",
        ref_str, class_->is_built_in() ? "OBJECT" :
          type->get_type_refd_last()->get_genname_own(my_scope).c_str(),
        ref_str, tmp_prefix, static_cast<unsigned long>(idx));
    }
    else {
      unreach = true; // else statement
      str = mputprintf(str, "goto %s_%lu;\n",
        tmp_prefix, static_cast<unsigned long>(idx));
    }
    return str;
  }

  char* SelectClassCase::generate_code_stmt(char* str, char*& def_glob_vars, char*& src_glob_vars,
                                            const char* tmp_prefix, size_t idx, bool& unreach)
  {
    if (unreach || always_false) {
      return str;
    }
    if (type == NULL) {
      unreach = true;
    }
    str = mputprintf(str, "%s_%lu:\n{\n",
      tmp_prefix, static_cast<unsigned long>(idx));
    if (debugger_active) {
      str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
    }
    str = block->generate_code(str, def_glob_vars, src_glob_vars);
    if(block->has_return() != StatementBlock::RS_YES) {
      str = mputprintf(str, "goto %s_end;\n}\n", tmp_prefix);
    }else {
      str = mputstr(str, "}\n");
    }
    return str;
  }

  void SelectClassCase::ilt_generate_code_stmt(ILT* ilt, const char* tmp_prefix,
                                               size_t idx, bool& unreach)
  {
    if (unreach || always_false) {
      return;
    }
    if (type == NULL) {
      unreach = true;
    }
    char*& str = ilt->get_out_branches();
    str = mputprintf(str, "%s_%lu:\n",
      tmp_prefix, static_cast<unsigned long>(idx));
    bool has_recv = block->has_receiving_stmt();
    if (!has_recv) {
      str = mputstr(str, "{\n");
      str = block->generate_code(str, ilt->get_out_def_glob_vars(),
        ilt->get_out_src_glob_vars());
    }
    else {
      block->ilt_generate_code(ilt);
    }
    str = mputprintf(str, "goto %s_end;\n", tmp_prefix);
    if (!has_recv) {
      str = mputstr(str, "}\n");
    }
  }

  void SelectClassCase::set_parent_path(WithAttribPath* p_path) {
    block->set_parent_path(p_path);
  }

  // =================================
  // ===== SelectClassCases
  // =================================

  SelectClassCases::~SelectClassCases()
  {
    for (size_t i = 0; i < sccs.size(); i++) {
      delete sccs[i];
    }
    sccs.clear();
  }

  SelectClassCases *SelectClassCases::clone() const
  {
    FATAL_ERROR("SelectClassCases::clone");
  }

  void SelectClassCases::add_scc(SelectClassCase *p_scc)
  {
    if (p_scc == NULL) {
      FATAL_ERROR("SelectClassCases::add_scc()");
    }
    sccs.add(p_scc);
  }

  void SelectClassCases::set_my_scope(Scope* p_scope)
  {
    for (size_t i = 0; i < sccs.size(); i++) {
      sccs[i]->set_my_scope(p_scope);
    }
  }

  void SelectClassCases::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for (size_t i = 0; i < sccs.size(); i++) {
      sccs[i]->set_fullname(p_fullname + ".scc_" + Int2string(i+1));
    }
  }

  void SelectClassCases::set_my_sb(StatementBlock* p_sb, size_t p_index)
  {
    for (size_t i = 0; i < sccs.size(); i++) {
      sccs[i]->get_block()->set_my_sb(p_sb, p_index);
    }
  }

  void SelectClassCases::set_my_def(Definition* p_def)
  {
    for (size_t i = 0; i < sccs.size(); i++) {
      sccs[i]->get_block()->set_my_def(p_def);
    }
  }

  void SelectClassCases::set_my_ags(AltGuards *p_ags)
  {
    for (size_t i = 0; i < sccs.size(); i++) {
      sccs[i]->get_block()->set_my_ags(p_ags);
    }
  }

  void SelectClassCases::set_my_laic_stmt(AltGuards* p_ags, Statement* p_loop_stmt)
  {
    for (size_t i = 0; i < sccs.size(); i++) {
      sccs[i]->get_block()->set_my_laic_stmt(p_ags, p_loop_stmt);
    }
  }

  StatementBlock::returnstatus_t SelectClassCases::has_return() const
  {
    StatementBlock::returnstatus_t ret_val = StatementBlock::RS_MAYBE;
    bool has_else = false;
    for (size_t i = 0; i < sccs.size(); i++) {
      SelectClassCase* scc = sccs[i];
      switch (scc->get_block()->has_return()) {
      case StatementBlock::RS_NO:
        if (ret_val == StatementBlock::RS_YES) {
          return StatementBlock::RS_MAYBE;
        }
        else {
          ret_val = StatementBlock::RS_NO;
        }
        break;
      case StatementBlock::RS_YES:
        if (ret_val == StatementBlock::RS_NO) {
          return StatementBlock::RS_MAYBE;
        }
        else {
          ret_val = StatementBlock::RS_YES;
        }
        break;
      default:
        return StatementBlock::RS_MAYBE;
      }
      if (scc->get_type() == NULL) {
        has_else = true;
        break;
      }
    }
    if (!has_else && ret_val == StatementBlock::RS_YES) {
      return StatementBlock::RS_MAYBE;
    }
    else {
      return ret_val;
    }
  }

  bool SelectClassCases::has_receiving_stmt() const
  {
    for (size_t i = 0; i < sccs.size(); i++) {
      if (sccs[i]->get_block()->has_receiving_stmt()) {
        return true;
      }
    }
    return false;
  }

  void SelectClassCases::chk(ClassTypeBody* p_ref_class)
  {
    bool unreach = false;
    for (size_t i = 0; i < sccs.size(); i++) {
      sccs[i]->chk(p_ref_class, unreach);
    }
  }

  void SelectClassCases::chk_allowed_interleave()
  {
    for (size_t i = 0; i < sccs.size(); i++) {
      sccs[i]->get_block()->chk_allowed_interleave();
    }
  }

  void SelectClassCases::set_code_section(GovernedSimple::code_section_t p_code_section)
  {
    for (size_t i = 0; i < sccs.size(); i++) {
      sccs[i]->set_code_section(p_code_section);
    }
  }

  char* SelectClassCases::generate_code(char* str, char*& def_glob_vars, char*& src_glob_vars,
                                        const char* tmp_prefix, const char* ref_str)
  {
    bool unreach = false;
    for (size_t i = 0; i < sccs.size(); i++) {
      str = sccs[i]->generate_code_if(str, tmp_prefix, ref_str, i, unreach);
      if (unreach) {
        break;
      }
    }
    if (!unreach) {
      str = mputprintf(str, "goto %s_end;\n", tmp_prefix);
    }
    unreach = false;
    for (size_t i = 0; i<sccs.size(); i++) {
      str = sccs[i]->generate_code_stmt(str, def_glob_vars, src_glob_vars,
        tmp_prefix, i, unreach);
      if (unreach) {
        break;
      }
    }
    if(!unreach || has_return() != StatementBlock::RS_YES) {
      str = mputprintf(str, "%s_end: /* empty */;\n", tmp_prefix);
    }
    return str;
  }

  void SelectClassCases::ilt_generate_code(ILT* ilt, const char* tmp_prefix,
                                           const char* ref_str)
  {
    char*& str = ilt->get_out_branches();
    bool unreach = false;
    for (size_t i = 0; i < sccs.size(); i++) {
      if (unreach) {
        break;
      }
      str = sccs[i]->generate_code_if(str, tmp_prefix, ref_str, i, unreach);
    }
    if (!unreach) {
      str = mputprintf(str, "goto %s_end;\n", tmp_prefix);
    }
    unreach = false;
    for (size_t i = 0; i < sccs.size(); i++) {
      if (unreach) {
        break;
      }
      sccs[i]->ilt_generate_code_stmt(ilt, tmp_prefix, i, unreach);
    }
    str = mputprintf(str, "%s_end:\n", tmp_prefix);
  }

  void SelectClassCases::set_parent_path(WithAttribPath* p_path) {
    for (size_t i = 0; i < sccs.size(); i++) {
      sccs[i]->set_parent_path(p_path);
    }
  }

  // =================================
  // ===== AltGuard
  // =================================

  AltGuard::AltGuard(Value *p_expr, Statement *p_stmt, StatementBlock *p_block)
    : altguardtype(AG_OP), expr(p_expr), stmt(p_stmt), block(p_block)
  {
    if (!p_stmt || !p_block) FATAL_ERROR("AltGuard::AltGuard()");
  }

  AltGuard::AltGuard(Value *p_expr, Reference *p_ref, StatementBlock *p_block)
    : altguardtype(AG_REF), expr(p_expr), ref(p_ref), block(p_block)
  {
    if (!p_ref) FATAL_ERROR("AltGuard::AltGuard()");
  }

  AltGuard::AltGuard(Value *p_expr, Value *p_v,
    Ttcn::TemplateInstances *p_t_list, StatementBlock *p_block)
    : altguardtype(AG_INVOKE), expr(p_expr)
    , block(p_block)
  {
    if(!p_v || !p_t_list) FATAL_ERROR("AltGuard::AltGuard()");
    invoke.v = p_v;
    invoke.t_list = p_t_list;
    invoke.ap_list = 0;
  }

  AltGuard::AltGuard(StatementBlock *p_block)
    : altguardtype(AG_ELSE), expr(0), dummy(0), block(p_block)
  {
    if (!p_block) FATAL_ERROR("AltGuard::AltGuard()");
  }

  AltGuard::~AltGuard()
  {
    switch(altguardtype) {
    case AG_OP:
      delete expr;
      delete stmt;
      delete block;
      break;
    case AG_REF:
      delete expr;
      delete ref;
      delete block;
      break;
    case AG_INVOKE:
      delete expr;
      delete invoke.v;
      delete invoke.t_list;
      delete invoke.ap_list;
      delete block;
      break;
    case AG_ELSE:
      delete block;
      break;
    default:
      FATAL_ERROR("AltGuard::~AltGuard()");
    } // switch
  }

  AltGuard *AltGuard::clone() const
  {
    FATAL_ERROR("AltGuard::clone");
  }

  void AltGuard::set_my_scope(Scope *p_scope)
  {
    switch(altguardtype) {
    case AG_OP:
      if(expr) expr->set_my_scope(p_scope);
      stmt->set_my_scope(p_scope);
      block->set_my_scope(p_scope);
      break;
    case AG_REF:
      if(expr) expr->set_my_scope(p_scope);
      ref->set_my_scope(p_scope);
      if(block) block->set_my_scope(p_scope);
      break;
    case AG_INVOKE:
      if(expr) expr->set_my_scope(p_scope);
      invoke.v->set_my_scope(p_scope);
      if(invoke.t_list) invoke.t_list->set_my_scope(p_scope);
      if(invoke.ap_list) invoke.ap_list->set_my_scope(p_scope);
      if(block) block->set_my_scope(p_scope);
      break;
    case AG_ELSE:
      block->set_my_scope(p_scope);
      break;
    default:
      FATAL_ERROR("AltGuard::set_my_scope()");
    } // switch
  }

  void AltGuard::set_my_sb(StatementBlock *p_sb, size_t p_index)
  {
    switch(altguardtype) {
    case AG_OP:
      stmt->set_my_sb(p_sb, p_index);
      block->set_my_sb(p_sb, p_index);
      break;
    case AG_REF:
      if(block) block->set_my_sb(p_sb, p_index);
      break;
    case AG_INVOKE:
      if(block) block->set_my_sb(p_sb, p_index);
      break;
    case AG_ELSE:
      block->set_my_sb(p_sb, p_index);
      break;
    default:
      FATAL_ERROR("AltGuard::set_my_sb()");
    } // switch
  }

  void AltGuard::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    switch(altguardtype) {
    case AG_OP:
      if(expr) expr->set_fullname(p_fullname+".expr");
      stmt->set_fullname(p_fullname+".stmt");
      block->set_fullname(p_fullname+".block");
      break;
    case AG_REF:
      if(expr) expr->set_fullname(p_fullname+".expr");
      ref->set_fullname(p_fullname+".ref");
      if(block) block->set_fullname(p_fullname+".block");
      break;
    case AG_INVOKE:
      if(expr) expr->set_fullname(p_fullname+".expr");
      invoke.v->set_fullname(p_fullname+".function");
      if(invoke.t_list) invoke.t_list->set_fullname(p_fullname+".argument");
      if(invoke.ap_list) invoke.ap_list->set_fullname(p_fullname+".argument");
      if(block) block->set_fullname(p_fullname+".block");
      break;
    case AG_ELSE:
      block->set_fullname(p_fullname+".elseblock");
      break;
    default:
      FATAL_ERROR("AltGuard::set_fullname()");
    } // switch
  }

  Value *AltGuard::get_guard_expr() const
  {
    if (altguardtype == AG_ELSE) FATAL_ERROR("AltGuard::get_guard_expr()");
    return expr;
  }

  Reference *AltGuard::get_guard_ref() const
  {
    if (altguardtype != AG_REF) FATAL_ERROR("AltGuard::get_guard_ref()");
    return ref;
  }

  Statement *AltGuard::get_guard_stmt() const
  {
    if (altguardtype != AG_OP) FATAL_ERROR("AltGuard::get_guard_stmt()");
    return stmt;
  }

  void AltGuard::set_my_def(Definition *p_def)
  {
    switch(altguardtype) {
    case AG_OP:
      stmt->set_my_def(p_def);
      block->set_my_def(p_def);
      break;
    case AG_REF:
      if(block) block->set_my_def(p_def);
      break;
    case AG_INVOKE:
      if(block) block->set_my_def(p_def);
      break;
    case AG_ELSE:
      block->set_my_def(p_def);
      break;
    default:
      FATAL_ERROR("AltGuard::set_my_def()");
    } // switch
  }

  void AltGuard::set_my_ags(AltGuards *p_ags)
  {
    switch (altguardtype) {
    case AG_OP:
      block->set_my_ags(p_ags);
      break;
    case AG_REF:
      if (block) block->set_my_ags(p_ags);
      break;
    case AG_INVOKE:
      if (block) block->set_my_ags(p_ags);
      break;
    case AG_ELSE:
      block->set_my_ags(p_ags);
      break;
    default:
      FATAL_ERROR("AltGuard::set_my_ags()");
    } // switch
  }

  void AltGuard::set_my_laic_stmt(AltGuards *p_ags, Statement *p_loop_stmt)
  {
    switch (altguardtype) {
    case AG_OP:
      block->set_my_laic_stmt(p_ags, p_loop_stmt);
      break;
    case AG_REF:
      if (block) block->set_my_laic_stmt(p_ags, p_loop_stmt);
      break;
    case AG_INVOKE:
      if (block) block->set_my_laic_stmt(p_ags, p_loop_stmt);
      break;
    case AG_ELSE:
      block->set_my_laic_stmt(p_ags, p_loop_stmt);
      break;
    default:
      FATAL_ERROR("AltGuard::set_my_laic_stmt()");
    } // switch
  }

  void AltGuard::chk()
  {
    switch(altguardtype) {
    case AG_OP:
      if (expr) {
	Error_Context cntxt(expr, "In guard expression");
        expr->chk_expr_bool(Type::EXPECTED_DYNAMIC_VALUE);
        expr->chk_expr_immutability();
      }
      {
	Error_Context cntxt(stmt, "In guard operation");
	stmt->chk();
      }
      block->chk();
      break;
    case AG_REF:
      if (expr) {
	Error_Context cntxt(expr, "In guard expression");
        expr->chk_expr_bool(Type::EXPECTED_DYNAMIC_VALUE);
        expr->chk_expr_immutability();
      }
      {
	Error_Context cntxt(ref, "In guard statement");
	Common::Assignment *t_ass = ref->get_refd_assignment();
	if (t_ass) {
	  if (t_ass->get_asstype() == Common::Assignment::A_ALTSTEP) {
	    ref->get_my_scope()->chk_runs_on_clause(t_ass, *ref, "call");
	  } else {
	    ref->error("Reference to an altstep was expected instead of %s",
	      t_ass->get_description().c_str());
	  }
	}
      }
      if (block) block->chk();
      break;
    case AG_INVOKE:
      if (expr) {
	Error_Context cntxt(expr, "In guard expression");
        expr->chk_expr_bool(Type::EXPECTED_DYNAMIC_VALUE);
        expr->chk_expr_immutability();
      }
      {
        if (!invoke.t_list) return; //already_checked
	Error_Context cntxt(ref, "In guard statement");
        switch(invoke.v->get_valuetype()){
        case Value::V_REFER:
          invoke.v->error(
            "A value of an altstep type was expected "
            "in the argument instead of a `refers' statement,"
            " which does not specify any function type");
          return;
        case Value::V_TTCN3_NULL:
          invoke.v->error(
            "A value of an altstep type was expected "
            "in the argument instead of a `null' value,"
            " which does not specify any function type");
          return;
        default:
          break;
        }
	Type *t = invoke.v->get_expr_governor_last();
	if (!t) return;
	switch (t->get_typetype()) {
	case Type::T_ERROR:
	  return;
	case Type::T_ALTSTEP:
	  break;
	default:
	  invoke.v->error("A value of type altstep was expected instead of "
	    "`%s'", t->get_typename().c_str());
	  return;
	}
        invoke.v->get_my_scope()->chk_runs_on_clause(t, *this, "call");
        Ttcn::FormalParList *fp_list = t->get_fat_parameters();
        invoke.ap_list = new Ttcn::ActualParList;
        bool is_erroneous = fp_list->chk_actual_parlist(invoke.t_list,
          invoke.ap_list);
        delete invoke.t_list;
        invoke.t_list = 0;
        if(is_erroneous) {
          delete invoke.ap_list;
          invoke.ap_list = 0;
        } else {
          invoke.ap_list->set_fullname(get_fullname());
	  invoke.ap_list->set_my_scope(invoke.v->get_my_scope());
        }
      }
      if (block) block->chk();
      break;
    case AG_ELSE:
      {
	Error_Context cntxt(this, "In else branch");
	block->chk();
	Statement *first_stmt = block->get_first_stmt();
	if (first_stmt && first_stmt->get_statementtype() ==
	    Statement::S_REPEAT)
	  first_stmt->warning("The first statement of the [else] branch is a "
	    "repeat statement. This will result in busy waiting");
      }
      break;
    default:
      FATAL_ERROR("AltGuard::chk()");
    } // switch
  }

  void AltGuard::set_code_section(
    GovernedSimple::code_section_t p_code_section)
  {
    switch(altguardtype) {
    case AG_OP:
      if (expr) expr->set_code_section(p_code_section);
      stmt->set_code_section(p_code_section);
      block->set_code_section(p_code_section);
      break;
    case AG_REF:
      if (expr) expr->set_code_section(p_code_section);
      ref->set_code_section(p_code_section);
      if (block) block->set_code_section(p_code_section);
      break;
    case AG_INVOKE:
      if (expr) expr->set_code_section(p_code_section);
      invoke.v->set_code_section(p_code_section);
      if(invoke.t_list) invoke.t_list->set_code_section(p_code_section);
      if(invoke.ap_list)
        for(size_t i = 0; i < invoke.ap_list->get_nof_pars(); i++)
            invoke.ap_list->get_par(i)->set_code_section(p_code_section);
      if (block) block->set_code_section(p_code_section);
      break;
    case AG_ELSE:
      block->set_code_section(p_code_section);
      break;
    default:
      FATAL_ERROR("AltGuard::set_fullname()");
    } // switch
  }

  void AltGuard::generate_code_invoke_instance(expression_struct *p_expr)
  {
    if (altguardtype != AG_INVOKE)
      FATAL_ERROR("AltGuard::generate_code_invoke_instance");
    Value *last_v = invoke.v->get_value_refd_last();
    if (last_v->get_valuetype() == Value::V_ALTSTEP) {
      Common::Assignment *altstep = last_v->get_refd_fat();
      p_expr->expr = mputprintf(p_expr->expr, "%s_instance(",
	altstep->get_genname_from_scope(invoke.v->get_my_scope()).c_str());
      invoke.ap_list->generate_code_alias(p_expr,
	altstep->get_FormalParList(), altstep->get_RunsOnType(), false);
    } else {
      invoke.v->generate_code_expr_mandatory(p_expr);
      p_expr->expr = mputstr(p_expr->expr, ".invoke(");
      Type* gov_last = invoke.v->get_expr_governor_last();
      invoke.ap_list->generate_code_alias(p_expr, 0,
	gov_last->get_fat_runs_on_type(), gov_last->get_fat_runs_on_self());
    }
    p_expr->expr = mputc(p_expr->expr, ')');
  }

  // =================================
  // ===== AltGuards
  // =================================

  AltGuards::~AltGuards()
  {
    for(size_t i=0; i<ags.size(); i++) delete ags[i];
    ags.clear();
    delete label;
    delete il_label_end;
  }

  AltGuards *AltGuards::clone() const
  {
    FATAL_ERROR("AltGuards::clone");
  }

  void AltGuards::add_ag(AltGuard *p_ag)
  {
    if(!p_ag)
      FATAL_ERROR("AltGuards::add_ag()");
    ags.add(p_ag);
  }

  void AltGuards::set_my_scope(Scope *p_scope)
  {
    my_scope = p_scope;
    for(size_t i=0; i<ags.size(); i++)
      ags[i]->set_my_scope(p_scope);
  }

  void AltGuards::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for(size_t i=0; i<ags.size(); i++)
      ags[i]->set_fullname(p_fullname+".ag_"+Int2string(i+1));
  }

  void AltGuards::set_my_sb(StatementBlock *p_sb, size_t p_index)
  {
    for(size_t i=0; i<ags.size(); i++)
      ags[i]->set_my_sb(p_sb, p_index);
  }

  void AltGuards::set_my_def(Definition *p_def)
  {
    for(size_t i=0; i<ags.size(); i++)
      ags[i]->set_my_def(p_def);
  }

  void AltGuards::set_my_ags(AltGuards *p_ags)
  {
    for(size_t i=0; i<ags.size(); i++)
      ags[i]->set_my_ags(p_ags);
  }

  void AltGuards::set_my_laic_stmt(AltGuards *p_ags, Statement *p_loop_stmt)
  {
    for(size_t i=0; i<ags.size(); i++)
      ags[i]->set_my_laic_stmt(p_ags, p_loop_stmt);
  }

  bool AltGuards::has_else() const
  {
    for (size_t i = 0; i < ags.size(); i++)
      if (ags[i]->get_type() == AltGuard::AG_ELSE) return true;
    return false;
  }

  StatementBlock::returnstatus_t AltGuards::has_return() const
  {
    StatementBlock::returnstatus_t ret_val = StatementBlock::RS_MAYBE;
    for (size_t i = 0; i < ags.size(); i++) {
      AltGuard *ag = ags[i];
      StatementBlock *block = ag->get_block();
      StatementBlock::returnstatus_t block_status;
      if (block) block_status = block->has_return();
      else block_status = StatementBlock::RS_NO;
      switch (block_status) {
      case StatementBlock::RS_NO:
	if (ret_val == StatementBlock::RS_YES) return StatementBlock::RS_MAYBE;
	else ret_val = StatementBlock::RS_NO;
	break;
      case StatementBlock::RS_YES:
	if (ret_val == StatementBlock::RS_NO) return StatementBlock::RS_MAYBE;
	else ret_val = StatementBlock::RS_YES;
	break;
      default:
	return StatementBlock::RS_MAYBE;
      }
      if (ag->get_type() == AltGuard::AG_ELSE) break;
    }
    return ret_val;
  }

  bool AltGuards::has_receiving_stmt() const
  {
    for(size_t i=0; i<ags.size(); i++)
      if(ags[i]->get_block()->has_receiving_stmt()) return true;
    return false;
  }

  void AltGuards::chk()
  {
    bool unreach_found = false;
    size_t nof_ags = ags.size();
    AltGuard *prev_ag = 0;
    for (size_t i = 0; i < nof_ags; i++) {
      AltGuard *ag = ags[i];
      ag->chk();
      if (!unreach_found && prev_ag &&
	  prev_ag->get_type() == AltGuard::AG_ELSE) {
	  ag->warning("Control never reaches this branch of alternative "
	    "because of the previous [else] branch");
	  unreach_found = true;
      }
      prev_ag = ag;
    }
  }

  void AltGuards::chk_allowed_interleave()
  {
    for (size_t i = 0; i < ags.size(); i++) {
      AltGuard *ag = ags[i];
      switch (ag->get_type()) {
      case AltGuard::AG_OP:
	break;
      case AltGuard::AG_REF:
      case AltGuard::AG_INVOKE:
        ag->error("Invocation of an altstep is not allowed within an "
	  "interleave statement");
	break;
      case AltGuard::AG_ELSE:
        ag->error("Else branch of an alternative is not allowed within an "
	  "interleave statement");
	break;
      default:
        FATAL_ERROR("AltGuards::chk_allowed_interleave()");
      }
      ag->get_block()->chk_allowed_interleave();
    }
  }

  void AltGuards::set_code_section(
    GovernedSimple::code_section_t p_code_section)
  {
    for (size_t i = 0; i < ags.size(); i++)
      ags[i]->set_code_section(p_code_section);
  }

  char *AltGuards::generate_code_alt(char *str, char*& def_glob_vars, char*& src_glob_vars,
                                     const Location& loc)
  {
    bool label_needed = has_repeat, has_else_branch = false;
    for (size_t i = 0; i < ags.size(); i++) {
      AltGuard *ag = ags[i];
      switch (ag->get_type()) {
      case AltGuard::AG_OP:
	// trigger may return ALT_REPEAT
        if (ag->get_guard_stmt()->can_repeat()) label_needed = true;
        break;
      case AltGuard::AG_REF:
      case AltGuard::AG_INVOKE:
	// an altstep may return ALT_REPEAT
        label_needed = true;
        break;
      case AltGuard::AG_ELSE:
        has_else_branch = true;
        break;
      default:
        FATAL_ERROR("AltGuards::generate_code_alt()");
      }
      if (has_else_branch) break;
    }
    // if there is no [else] branch the defaults may return ALT_REPEAT
    if (!has_else_branch) label_needed = true;
    // opening bracket of the statement block
    str = mputstr(str, "{\n");
    // the label name is also used for prefixing local variables
    if (!my_scope || label) FATAL_ERROR("AltGuards::generate_code_alt()");
    label = new string(my_scope->get_scope_mod_gen()->get_temporary_id());
    const char *label_str = label->c_str();
    if (label_needed) str = mputprintf(str, "%s:\n", label_str);
    // temporary variables used for caching of status codes
    for (size_t i = 0; i < ags.size(); i++) {
      AltGuard *ag = ags[i];
      if (ag->get_type() == AltGuard::AG_ELSE) break;
      str = mputprintf(str, "alt_status %s_alt_flag_%lu = %s;\n",
	label_str, static_cast<unsigned long>( i ),
        ag->get_guard_expr() ? "ALT_UNCHECKED" : "ALT_MAYBE");
    }
    if (!has_else_branch) {
      str = mputprintf(str, "alt_status %s_default_flag = ALT_MAYBE;\n",
	label_str);
    }
    // the first snapshot is taken in non-blocking mode
    // and opening infinite for() loop
    str = mputstr(str, "TTCN_Snapshot::take_new(FALSE);\n"
      "for ( ; ; ) {\n");
    for (size_t i = 0; i < ags.size(); i++) {
      AltGuard *ag = ags[i];
      AltGuard::altguardtype_t agtype = ag->get_type();
      if (agtype == AltGuard::AG_ELSE) {
        // an else branch was found
        str = mputstr(str, "TTCN_Snapshot::else_branch_reached();\n");
        StatementBlock *block = ag->get_block();
        if (block->get_nof_stmts() > 0) {
          str = mputstr(str, "{\n");
          if (debugger_active) {
            str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
          }
          str = block->generate_code(str, def_glob_vars, src_glob_vars);
          str = mputstr(str, "}\n");
        }
        // jump out of the infinite for() loop
        if (block->has_return() != StatementBlock::RS_YES)
          str = mputstr(str, "break;\n");
        // do not generate code for further branches
        break;
      } else {
        Value *guard_expr = ag->get_guard_expr();
        if (guard_expr) {
          // the branch has a boolean guard expression
          str = mputprintf(str, "if (%s_alt_flag_%lu == ALT_UNCHECKED) {\n",
            label_str, static_cast<unsigned long>( i ));
          str = guard_expr->update_location_object(str);
          expression_struct expr;
          Code::init_expr(&expr);
          guard_expr->generate_code_expr(&expr);
          str = mputstr(str, expr.preamble);
          str = mputprintf(str, "if (%s) %s_alt_flag_%lu = ALT_MAYBE;\n"
            "else %s_alt_flag_%lu = ALT_NO;\n", expr.expr, label_str,
            static_cast<unsigned long>( i ), label_str, static_cast<unsigned long>( i ));
          str = mputstr(str, expr.postamble);
          Code::free_expr(&expr);
          str = mputstr(str, "}\n");
        }
        // evaluation of guard operation or altstep
        str = mputprintf(str, "if (%s_alt_flag_%lu == ALT_MAYBE) {\n",
          label_str, static_cast<unsigned long>( i ));
        // indicates whether the guard operation might return ALT_REPEAT
        bool can_repeat;
        expression_struct expr;
        Code::init_expr(&expr);
        expr.expr = mputprintf(expr.expr, "%s_alt_flag_%lu = ", label_str,
          static_cast<unsigned long>( i ));
        switch (agtype) {
        case AltGuard::AG_OP: {
          // the guard operation is a receiving statement
          Statement *stmt = ag->get_guard_stmt();
          str = stmt->update_location_object(str);
          stmt->generate_code_expr(&expr);
          can_repeat = stmt->can_repeat();
          break; }
        case AltGuard::AG_REF: {
          // the guard operation is an altstep instance
          Reference *ref = ag->get_guard_ref();
          str = ref->update_location_object(str);
          Common::Assignment *altstep = ref->get_refd_assignment();
          expr.expr = mputprintf(expr.expr, "%s_instance(",
            altstep->get_genname_from_scope(my_scope).c_str());
          ref->get_parlist()->generate_code_alias(&expr,
            altstep->get_FormalParList(), altstep->get_RunsOnType(), false);
          expr.expr = mputc(expr.expr, ')');
          can_repeat = true;
          break; }
        case AltGuard::AG_INVOKE: {
          // the guard operation is an altstep invocation
          str = ag->update_location_object(str);
          ag->generate_code_invoke_instance(&expr);
          can_repeat = true;
          break; }
        default:
          FATAL_ERROR("AltGuards::generate_code_alt()");
        }
        str = Code::merge_free_expr(str, &expr);
        if (can_repeat) {
          str = mputprintf(str, "if (%s_alt_flag_%lu == ALT_REPEAT) goto %s;\n",
            label_str, static_cast<unsigned long>( i ), label_str);
        }
        if (agtype == AltGuard::AG_REF || agtype == AltGuard::AG_INVOKE) {
          str = mputprintf(str, "if (%s_alt_flag_%lu == ALT_BREAK) break;\n",
             label_str, static_cast<unsigned long>( i ));
        }
        // execution of statement block if the guard was successful
        str = mputprintf(str, "if (%s_alt_flag_%lu == ALT_YES) ", label_str,
          static_cast<unsigned long>( i ));
        StatementBlock *block = ag->get_block();
        if (block && block->get_nof_stmts() > 0) {
          str = mputstr(str, "{\n");
          if (debugger_active) {
            str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
          }
          str = block->generate_code(str, def_glob_vars, src_glob_vars);
          if (block->has_return() != StatementBlock::RS_YES)
            str = mputstr(str, "break;\n");
          str = mputstr(str, "}\n");
        } else str = mputstr(str, "break;\n");
        // closing of if() block
        str = mputstr(str, "}\n");
      } //if
    } // for
    if (!has_else_branch) {
      // calling of defaults
      str = mputprintf(str, "if (%s_default_flag == ALT_MAYBE) {\n"
	"%s_default_flag = TTCN_Default::try_altsteps();\n"
	"if (%s_default_flag == ALT_YES || %s_default_flag == ALT_BREAK)"
          " break;\n"
	"else if (%s_default_flag == ALT_REPEAT) goto %s;\n"
	"}\n",
        label_str, label_str, label_str, label_str, label_str, label_str);
      str = loc.update_location_object(str);
      // error handling and taking the next snapshot in blocking mode
      str = mputstr(str, "if (");
      for (size_t i = 0; i < ags.size(); i++)
	str = mputprintf(str, "%s_alt_flag_%lu == ALT_NO && ", label_str,
          static_cast<unsigned long>( i ));
      str = mputprintf(str,"%s_default_flag == ALT_NO) "
	  "TTCN_error(\"None of the branches can be chosen in the alt "
	  "statement in file ", label_str);
      str = Code::translate_string(str, loc.get_filename());
      size_t first_line = loc.get_first_line(), last_line = loc.get_last_line();
      if (first_line < last_line) str = mputprintf(str,
	" between lines %lu and %lu", first_line, last_line);
      else str = mputprintf(str, ", line %lu", first_line);
      str = mputstr(str, ".\");\n"
	"TTCN_Snapshot::take_new(TRUE);\n");
    }
    // end of for() statement and the statement block
    str = mputstr(str, "}\n"
      "}\n");
    return str;
  }

  char *AltGuards::generate_code_altstep(char *str, char*& def_glob_vars, char*& src_glob_vars)
  {
    if (!my_scope) FATAL_ERROR("AltGuards::generate_code_altstep()");
    Common::Module *my_mod = my_scope->get_scope_mod_gen();
    bool has_else_branch = has_else();
    if (!has_else_branch) {
      str = mputstr(str, "alt_status ret_val = ALT_NO;\n");
    }
    for (size_t i = 0; i < ags.size(); i++) {
      AltGuard *ag = ags[i];
      AltGuard::altguardtype_t agtype = ag->get_type();
      if (agtype == AltGuard::AG_ELSE) {
      // an else branch was found
      str = mputstr(str, "TTCN_Snapshot::else_branch_reached();\n");
      StatementBlock *block = ag->get_block();
      if (block->get_nof_stmts() > 0) {
        str = mputstr(str, "{\n");
        if (debugger_active) {
          str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
        }
        str = block->generate_code(str, def_glob_vars, src_glob_vars);
        str = mputstr(str, "}\n");
      }
      if (block->has_return() != StatementBlock::RS_YES)
        str = mputstr(str, "return ALT_YES;\n");
      // do not generate code for further branches
      break;
      } else {
        size_t blockcount = 0;
      Value *guard_expr = ag->get_guard_expr();
      if (guard_expr) {
        // the branch has a boolean guard expression
        str = guard_expr->update_location_object(str);
        str = guard_expr->generate_code_tmp(str, "if (", blockcount);
        str = mputstr(str, ") {\n");
        blockcount++;
      }
      // indicates whether the guard operation might return ALT_REPEAT
      bool can_repeat;
      expression_struct expr;
      Code::init_expr(&expr);
      switch (agtype) {
      case AltGuard::AG_OP: {
        // the guard operation is a receiving statement
        Statement *stmt = ag->get_guard_stmt();
        str = stmt->update_location_object(str);
        stmt->generate_code_expr(&expr);
        can_repeat = stmt->can_repeat();
        break; }
      case AltGuard::AG_REF: {
        // the guard operation is an altstep instance
        Reference *ref = ag->get_guard_ref();
        str = ref->update_location_object(str);
        Common::Assignment *altstep = ref->get_refd_assignment();
        expr.expr = mputprintf(expr.expr, "%s_instance(",
          altstep->get_genname_from_scope(my_scope).c_str());
        ref->get_parlist()->generate_code_alias(&expr,
          altstep->get_FormalParList(), altstep->get_RunsOnType(), false);
        expr.expr = mputc(expr.expr, ')');
        can_repeat = true;
        break; }
      case AltGuard::AG_INVOKE: {
          str = ag->update_location_object(str);
          ag->generate_code_invoke_instance(&expr);
          can_repeat = true;
        break; }
      default:
        FATAL_ERROR("AltGuards::generate_code_altstep()");
        }
      if (expr.preamble || expr.postamble) {
        if (blockcount == 0) {
          // open a statement block if it is not done so far
          str = mputstr(str, "{\n");
          blockcount++;
        }
        const string& tmp_id = my_mod->get_temporary_id();
        const char *tmp_id_str = tmp_id.c_str();
        str = mputprintf(str, "alt_status %s;\n"
          "{\n", tmp_id_str);
        str = mputstr(str, expr.preamble);
        str = mputprintf(str, "%s = %s;\n", tmp_id_str, expr.expr);
        str = mputstr(str, expr.postamble);
        str = mputprintf(str, "}\n"
          "switch (%s) {\n", tmp_id_str);
      } else {
        str = mputprintf(str, "switch (%s) {\n", expr.expr);
      }
      Code::free_expr(&expr);
      str = mputstr(str, "case ALT_YES:\n");
      StatementBlock *block = ag->get_block();
      if (block && block->get_nof_stmts() > 0) {
        str = mputstr(str, "{\n");
        if (debugger_active) {
          str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
        }
        str = block->generate_code(str, def_glob_vars, src_glob_vars);
        str = mputstr(str, "}\n");
      }
      if (!block || block->has_return() != StatementBlock::RS_YES)
        str = mputstr(str, "return ALT_YES;\n");
      if (can_repeat)
        str = mputstr(str, "case ALT_REPEAT:\n"
          "return ALT_REPEAT;\n");
      if (agtype == AltGuard::AG_REF || agtype == AltGuard::AG_INVOKE) {
        str = mputprintf(str, "case ALT_BREAK:\n"
        "return ALT_BREAK;\n");
      }
      if (!has_else_branch)
        str = mputstr(str, "case ALT_MAYBE:\n"
          "ret_val = ALT_MAYBE;\n");
      str = mputstr(str, "default:\n"
        "break;\n"
        "}\n");
      // closing statement blocks
      for ( ; blockcount > 0; blockcount--) str = mputstr(str, "}\n");
      }
    }
    if (!has_else_branch) str = mputstr(str, "return ret_val;\n");
    return str;
  }

  char* AltGuards::generate_code_call_body(char *str, char*& def_glob_vars, char*& src_glob_vars,
                                           const Location& loc,
                                           const string& temp_id, bool in_interleave)
  {
    if (label) FATAL_ERROR("AltGuards::generate_code_call_body()");
    label = new string(temp_id);
    const char *label_str = temp_id.c_str();
    // label is needed only if there is a repeat statement in the branches
    if (has_repeat) str = mputprintf(str, "%s:\n", label_str);
    // temporary variables used for caching of status codes
    for (size_t i = 0; i < ags.size(); i++)
      str = mputprintf(str, "alt_status %s_alt_flag_%lu = %s;\n",
                       label_str, static_cast<unsigned long>( i ),
                       ags[i]->get_guard_expr()?"ALT_UNCHECKED":"ALT_MAYBE");
    str = loc.update_location_object(str);
    // the first snapshot is taken in non-blocking mode
    // and opening infinite for() loop
    str = mputstr(str, "TTCN_Snapshot::take_new(FALSE);\n"
                  "for ( ; ; ) {\n"); // (1)
    for (size_t i = 0; i < ags.size(); i++) {
      AltGuard *ag = ags[i];
      if (ag->get_type() != AltGuard::AG_OP)
	FATAL_ERROR("AltGuards::generate_code_call_body()");
      Value *guard_expr = ag->get_guard_expr();
      if (guard_expr) {
	// the branch has a boolean guard expression
	str = mputprintf(str,
                         "if (%s_alt_flag_%lu == ALT_UNCHECKED) {\n", // (2)
                         label_str, static_cast<unsigned long>( i ));
	str = guard_expr->update_location_object(str);
	expression_struct expr;
	Code::init_expr(&expr);
	guard_expr->generate_code_expr(&expr);
	str = mputstr(str, expr.preamble);
	str = mputprintf(str, "if (%s) %s_alt_flag_%lu = ALT_MAYBE;\n"
                         "else %s_alt_flag_%lu = ALT_NO;\n",
                         expr.expr, label_str, static_cast<unsigned long>( i ),
                         label_str, static_cast<unsigned long>( i ));
	str = mputstr(str, expr.postamble);
	Code::free_expr(&expr);
	str = mputstr(str, "}\n"); // (2)
      }
      // evaluation of guard operation
      str = mputprintf(str, "if (%s_alt_flag_%lu == ALT_MAYBE) {\n", // (2)
                       label_str, static_cast<unsigned long>( i ));
      expression_struct expr;
      Code::init_expr(&expr);
      expr.expr = mputprintf(expr.expr, "%s_alt_flag_%lu = ", label_str,
        static_cast<unsigned long>( i ));
      Statement *stmt = ag->get_guard_stmt();
      str = stmt->update_location_object(str);
      stmt->generate_code_expr(&expr);
      str = Code::merge_free_expr(str, &expr);
      // execution of statement block if the guard was successful
      str = mputprintf(str, "if (%s_alt_flag_%lu == ALT_YES) ", label_str,
        static_cast<unsigned long>( i ));
      StatementBlock *block = ag->get_block();
      if(in_interleave) {
        if(block && block->get_nof_stmts() > 0) {
          if(block->has_receiving_stmt()) {
            str = mputprintf(str, "goto %s_branch%lu;\n",
                             label_str, static_cast<unsigned long>( i ));
          }
          else {
            str = mputstr(str, "{\n"); // (3)
            str = block->generate_code(str, def_glob_vars, src_glob_vars);
            str = mputprintf(str, "goto %s_end;\n"
                             "}\n", // (3)
                             label_str);
          }
        }
        else str = mputprintf(str, "goto %s_end;\n", label_str);
      }
      else {
        if (block && block->get_nof_stmts() > 0) {
          str = mputstr(str, "{\n"); // (3)
          str = block->generate_code(str, def_glob_vars, src_glob_vars);
            if (block->has_return() != StatementBlock::RS_YES)
              str = mputstr(str, "break;\n");
          str = mputstr(str, "}\n"); // (3)
        }
        else str = mputstr(str, "break;\n");
      }
      // closing of if() block
      str = mputstr(str, "}\n"); // (2)
    }
    str = loc.update_location_object(str);
    // error handling and taking the next snapshot in blocking mode
    str = mputstr(str, "if (");
    for (size_t i = 0; i < ags.size(); i++) {
      if (i > 0) str = mputstr(str, " && ");
      str = mputprintf(str, "%s_alt_flag_%lu == ALT_NO", label_str,
        static_cast<unsigned long>( i ));
    }
    str = mputstr(str, ") TTCN_error(\"None of the branches can be chosen in "
      "the response and exception handling part of call statement in file ");
    str = Code::translate_string(str, loc.get_filename());
    size_t first_line = loc.get_first_line(), last_line = loc.get_last_line();
    if (first_line < last_line) str = mputprintf(str,
      " between lines %lu and %lu", first_line, last_line);
    else str = mputprintf(str, ", line %lu", first_line);
    str = mputstr(str, ".\");\n"
      "TTCN_Snapshot::take_new(TRUE);\n"
      "}\n"); // (1) for
    return str;
  }

  void AltGuards::ilt_generate_code_call_body(ILT *ilt, const char *label_str)
  {
    char*& str=ilt->get_out_branches();
    for(size_t i=0; i<ags.size(); i++) {
      StatementBlock *block = ags[i]->get_block();
      if (block && block->has_receiving_stmt()) {
        str = mputprintf(str, "%s_branch%lu:\n", label_str, static_cast<unsigned long>( i ));
        block->ilt_generate_code(ilt);
        str = mputprintf(str, "goto %s_end;\n", label_str);
      }
    } // for i
  }

} // namespace Ttcn
