From fcbf96e42b4cd89dcbdaac4a7d71a486613282a6 Mon Sep 17 00:00:00 2001
From: Botond Baranyi <botond.baranyi@ericsson.com>
Date: Mon, 27 Feb 2017 10:54:26 +0100
Subject: [PATCH] Implemented the '@update' statement for changing erroneous
 attributes (bug 511903)

Change-Id: I2fe9654e659c08cbd8415b4aa2cc5b6b52f67709
Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com>
---
 compiler2/Setting.cc                          |  17 +
 compiler2/Setting.hh                          |  19 +-
 compiler2/Type.hh                             |   3 +-
 compiler2/Type_codegen.cc                     |  21 +-
 compiler2/Value.cc                            |  13 +-
 compiler2/ttcn3/AST_ttcn3.cc                  | 100 ++++--
 compiler2/ttcn3/AST_ttcn3.hh                  |   5 +-
 compiler2/ttcn3/Attributes.cc                 | 139 ++++++--
 compiler2/ttcn3/Attributes.hh                 |  39 ++-
 compiler2/ttcn3/ILT.cc                        |  28 +-
 compiler2/ttcn3/ILT.hh                        |  10 +-
 compiler2/ttcn3/Statement.cc                  | 309 +++++++++++++-----
 compiler2/ttcn3/Statement.hh                  |  80 +++--
 compiler2/ttcn3/TtcnTemplate.cc               |   6 -
 compiler2/ttcn3/compiler.l                    |   2 +-
 compiler2/ttcn3/compiler.y                    |  22 +-
 .../Semantic_Analyser/Makefile.semantic       |   2 +-
 .../erroneous_attributes/.gitignore           |   2 +
 .../ErroneousAttributes_SE.ttcn               | 178 ++++++++++
 .../erroneous_attributes/Makefile             |  12 +
 .../Semantic_Analyser/erroneous_attributes/t  |   9 +
 regression_test/negativeTest/Makefile         |   2 +-
 regression_test/negativeTest/NegTest_JSON.cfg |   1 +
 .../negativeTest/NegTest_JSON.ttcn            |  24 ++
 .../negativeTest/NegTest_Update.ttcn          | 231 +++++++++++++
 regression_test/negativeTest/NegTest_all.cfg  |   1 +
 26 files changed, 1072 insertions(+), 203 deletions(-)
 create mode 100644 function_test/Semantic_Analyser/erroneous_attributes/.gitignore
 create mode 100644 function_test/Semantic_Analyser/erroneous_attributes/ErroneousAttributes_SE.ttcn
 create mode 100644 function_test/Semantic_Analyser/erroneous_attributes/Makefile
 create mode 100755 function_test/Semantic_Analyser/erroneous_attributes/t
 create mode 100644 regression_test/negativeTest/NegTest_Update.ttcn

diff --git a/compiler2/Setting.cc b/compiler2/Setting.cc
index 83246cc7f..5dc5c31a3 100644
--- a/compiler2/Setting.cc
+++ b/compiler2/Setting.cc
@@ -36,6 +36,7 @@
 #include "Int.hh"
 #include "main.hh"
 #include "ttcn3/profiler.h"
+#include "ttcn3/Attributes.hh"
 
 namespace Common {
 
@@ -481,6 +482,22 @@ namespace Common {
   // =================================
   // ===== GovernedSimple
   // =================================
+  
+  GovernedSimple::~GovernedSimple()
+  {
+    delete err_descrs;
+  }
+  
+  void GovernedSimple::add_err_descr(Ttcn::Statement* p_update_statement,
+                                     Ttcn::ErroneousDescriptor* p_err_descr)
+  {
+    if (p_err_descr != NULL) {
+      if (err_descrs == NULL) {
+        err_descrs = new Ttcn::ErroneousDescriptors;
+      }
+      err_descrs->add(p_update_statement, p_err_descr);
+    }
+  }
 
   string GovernedSimple::get_lhs_name() const
   {
diff --git a/compiler2/Setting.hh b/compiler2/Setting.hh
index c0f37a8fb..1c84988b8 100644
--- a/compiler2/Setting.hh
+++ b/compiler2/Setting.hh
@@ -57,7 +57,9 @@ namespace Ttcn {
   class RunsOnScope;
   class StatementBlock;
   struct ErroneousDescriptor;
+  class ErroneousDescriptors;
   class transparency_holder;
+  class Statement;
 } // namespace Ttcn
 
 namespace Common {
@@ -466,18 +468,19 @@ public:
      */
     bool code_generated;
   protected: // Derived classes need access to the copy c-tor
-    Ttcn::ErroneousDescriptor* err_descr; // not owned, used by negative testing
+    Ttcn::ErroneousDescriptors* err_descrs; // owned, used by negative testing
     GovernedSimple(const GovernedSimple& p) : Governed(p),
       genname_prefix(p.genname_prefix), code_section(p.code_section),
-      code_generated(false), err_descr(NULL), needs_conversion(false) { }
+      code_generated(false), err_descrs(NULL), needs_conversion(false) { }
     bool needs_conversion; /**< Type conversion needed. */
   private:
     /** Assignment disabled */
     GovernedSimple& operator=(const GovernedSimple& p);
   public:
     GovernedSimple(settingtype_t p_st) : Governed(p_st), genname_prefix(0),
-      code_section(CS_UNKNOWN), code_generated(false), err_descr(NULL),
+      code_section(CS_UNKNOWN), code_generated(false), err_descrs(NULL),
       needs_conversion(false) { }
+    ~GovernedSimple();
 
     /** Sets attribute \a genname_prefix to \a p_genname_prefix. For efficiency
      * reasons the string itself is not copied, thus it must point to a
@@ -498,11 +501,11 @@ public:
     /** Sets the flag \a code_generated to true. */
     void set_code_generated() { code_generated = true; }
 
-    /** Sets the err_descr if the template or value has negative testing */
-    void set_err_descr(Ttcn::ErroneousDescriptor* p_err_descr)
-      { err_descr = p_err_descr; }
-    Ttcn::ErroneousDescriptor* get_err_descr() const
-      { return err_descr; }
+    /** Adds an error descriptor to the template or value (for negative testing) */
+    void add_err_descr(Ttcn::Statement* p_update_statement,
+      Ttcn::ErroneousDescriptor* p_err_descr);
+    Ttcn::ErroneousDescriptors* get_err_descr() const
+      { return err_descrs; }
 
     /** has_single_expr() to return false. */
     inline void set_needs_conversion() { needs_conversion = true; }
diff --git a/compiler2/Type.hh b/compiler2/Type.hh
index 09173b9e6..0d7ab4ce4 100644
--- a/compiler2/Type.hh
+++ b/compiler2/Type.hh
@@ -1157,7 +1157,8 @@ namespace Common {
      * exported with name \a name. This function shall be used when there is no
      * Value or Template object in the AST (e.g. in case of variables). */
     void generate_code_object(const_def *cdef, Scope* p_scope,
-      const string& name, const char *prefix, bool is_template);
+      const string& name, const char *prefix, bool is_template,
+      bool has_err_descr);
     /** Generates the declaration and definition of a C++ value or template
      * object governed by \a this into \a cdef based on the attributes of
      * \a p_setting. */
diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc
index c7246a7ac..e75ad4657 100644
--- a/compiler2/Type_codegen.cc
+++ b/compiler2/Type_codegen.cc
@@ -2820,7 +2820,7 @@ bool Type::has_done_attribute()
 }
 
 void Type::generate_code_object(const_def *cdef, Scope *p_scope,
-  const string& name, const char *prefix, bool is_template)
+  const string& name, const char *prefix, bool is_template, bool has_err_descr)
 {
   string type_name;
   if (is_template) type_name = get_genname_template(p_scope);
@@ -2830,12 +2830,12 @@ void Type::generate_code_object(const_def *cdef, Scope *p_scope,
   if (prefix) {
     cdef->decl = mputprintf(cdef->decl, "extern const %s& %s;\n",
       type_name_str, name_str);
-    if (split_to_slices) {
+    if (split_to_slices || has_err_descr) {
       cdef->decl = mputprintf(cdef->decl, "extern %s %s%s;\n", type_name_str, prefix, name_str);
     }
     cdef->def = mputprintf(cdef->def, "%s%s %s%s;\n"
-      "const %s& %s = %s%s;\n", split_to_slices ? "" : "static ", type_name_str, prefix, name_str,
-        type_name_str, name_str, prefix, name_str);
+      "const %s& %s = %s%s;\n", split_to_slices || has_err_descr ? "" : "static ",
+      type_name_str, prefix, name_str, type_name_str, name_str, prefix, name_str);
   } else {
     cdef->decl = mputprintf(cdef->decl, "extern %s %s;\n",
       type_name_str, name_str);
@@ -2856,13 +2856,18 @@ void Type::generate_code_object(const_def *cdef, GovernedSimple *p_setting)
   default:
     FATAL_ERROR("Type::generate_code_object()");
   }
-  if (p_setting->get_err_descr()) {
-    cdef->def = p_setting->get_err_descr()->generate_code_str(cdef->def, cdef->decl,
-      p_setting->get_genname_prefix() + p_setting->get_genname_own(), FALSE);
+  if (p_setting->get_err_descr() != NULL &&
+      p_setting->get_err_descr()->has_descr(NULL)) {
+    cdef->def = p_setting->get_err_descr()->generate_code_str(NULL, cdef->def,
+      cdef->decl, p_setting->get_genname_prefix() + p_setting->get_genname_own());
   }
+  // allways pass 'true' to the 'has_err_descr' parameter while in Runtime2, not
+  // just if the value/template has erroneous descriptors, so adding an '@update'
+  // statement in a different module does not require this module's code to be
+  // regenerated
   generate_code_object(cdef, p_setting->get_my_scope(),
     p_setting->get_genname_own(), p_setting->get_genname_prefix(),
-    is_template);
+    is_template, use_runtime_2);
 }
 
 void Type::generate_json_schema(JSON_Tokenizer& json, bool embedded, bool as_value)
diff --git a/compiler2/Value.cc b/compiler2/Value.cc
index 4e56647c1..2784f2a63 100644
--- a/compiler2/Value.cc
+++ b/compiler2/Value.cc
@@ -10019,8 +10019,8 @@ error:
       default:
 	break;
       }
-      if (v->err_descr) { // FIXME: make this work
-        v->err_descr->chk_recursions(refch);
+      if (v->err_descrs) { // FIXME: make this work
+        v->err_descrs->chk_recursions(refch);
       }
     }
     recurs_checked = true;
@@ -11677,8 +11677,8 @@ error:
   char *Value::generate_code_init(char *str, const char *name)
   {
     if (get_code_generated()) return str;
-    if (err_descr) {
-      str = err_descr->generate_code_init_str(str, string(name) + "_err_descr");
+    if (err_descrs != NULL && err_descrs->has_descr(NULL)) {
+      str = err_descrs->generate_code_init_str(NULL, str, string(name));
     }
     switch (valuetype) {
     case V_NULL:
@@ -11753,8 +11753,9 @@ error:
     default:
       FATAL_ERROR("Value::generate_code_init()");
     }
-    if (err_descr) {
-      str = mputprintf(str, "%s.set_err_descr(&%s_err_descr);\n", name, name);
+    if (err_descrs != NULL && err_descrs->has_descr(NULL)) {
+      str = mputprintf(str, "%s.set_err_descr(&%s_%lu_err_descr);\n", name,
+        name, (unsigned long) err_descrs->get_descr_index(NULL));
     }
     set_code_generated();
     return str;
diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc
index f3568746a..db440a8e8 100644
--- a/compiler2/ttcn3/AST_ttcn3.cc
+++ b/compiler2/ttcn3/AST_ttcn3.cc
@@ -2171,7 +2171,8 @@ namespace Ttcn {
         "debug_scope.initial_snapshot();\n", module_dispname);
     }
     target->functions.control =
-      block->generate_code(target->functions.control);
+      block->generate_code(target->functions.control, target->header.global_vars,
+      target->source.global_vars);
     target->functions.control = mputstr(target->functions.control,
       "TTCN_Runtime::end_controlpart();\n");
   }
@@ -3112,18 +3113,21 @@ namespace Ttcn {
     return false;
   }
 
-  void Definition::chk_erroneous_attr()
+  ErroneousAttributes* Definition::chk_erroneous_attr(WithAttribPath* p_attrib_path, Type* p_type,
+                                                      Scope* p_scope, string p_fullname,
+                                                      bool in_update_stmt)
   {
-    if (!w_attrib_path) return;
-    const Ttcn::MultiWithAttrib* attribs = w_attrib_path->get_local_attrib();
-    if (!attribs) return;
+    if (!p_attrib_path) return NULL;
+    const Ttcn::MultiWithAttrib* attribs = p_attrib_path->get_local_attrib();
+    if (!attribs) return NULL;
+    ErroneousAttributes* erroneous_attrs = NULL;
     for (size_t i = 0; i < attribs->get_nof_elements(); i++) {
       const Ttcn::SingleWithAttrib* act_attr = attribs->get_element(i);
       if (act_attr->get_attribKeyword()==Ttcn::SingleWithAttrib::AT_ERRONEOUS) {
         if (!use_runtime_2) {
-          error("`erroneous' attributes can be used only with the Function Test Runtime");
-          note("If you need negative testing use the -R flag when generating the makefile");
-          return;
+          attribs->error("`erroneous' attributes can be used only with the Function Test Runtime");
+          attribs->note("If you need negative testing use the -R flag when generating the makefile");
+          return NULL;
         }
         size_t nof_qualifiers = act_attr->get_attribQualifiers() ? act_attr->get_attribQualifiers()->get_nof_qualifiers() : 0;
         dynamic_array<Type*> refd_type_array(nof_qualifiers); // only the qualifiers pointing to existing fields will be added to erroneous_attrs objects
@@ -3133,12 +3137,12 @@ namespace Ttcn {
           // check if qualifiers point to existing fields
           for (size_t qi=0; qi<nof_qualifiers; qi++) {
             Qualifier* act_qual = const_cast<Qualifier*>(act_attr->get_attribQualifiers()->get_qualifier(qi));
-            act_qual->set_my_scope(get_my_scope());
-            Type* field_type = get_Type()->get_field_type(act_qual, Type::EXPECTED_CONSTANT);
+            act_qual->set_my_scope(p_scope);
+            Type* field_type = p_type->get_field_type(act_qual, Type::EXPECTED_CONSTANT);
             if (field_type) {
               dynamic_array<size_t> subrefs_array;
               dynamic_array<Type*> type_array;
-              bool valid_indexes = get_Type()->get_subrefs_as_array(act_qual, subrefs_array, type_array);
+              bool valid_indexes = p_type->get_subrefs_as_array(act_qual, subrefs_array, type_array);
               if (!valid_indexes) field_type = NULL;
               if (act_qual->refers_to_string_element()) {
                 act_qual->error("Reference to a string element cannot be used in this context");
@@ -3152,12 +3156,12 @@ namespace Ttcn {
         ErroneousAttributeSpec* err_attr_spec = ttcn3_parse_erroneous_attr_spec_string(
           act_attr->get_attribSpec().get_spec().c_str(), act_attr->get_attribSpec());
         if (err_attr_spec) {
-          if (!erroneous_attrs) erroneous_attrs = new ErroneousAttributes(get_Type());
+          if (!erroneous_attrs) erroneous_attrs = new ErroneousAttributes(p_type);
           // attr.spec will be owned by erroneous_attrs object
           erroneous_attrs->add_spec(err_attr_spec);
-          err_attr_spec->set_fullname(get_fullname());
-          err_attr_spec->set_my_scope(get_my_scope());
-          err_attr_spec->chk();
+          err_attr_spec->set_fullname(p_fullname);
+          err_attr_spec->set_my_scope(p_scope);
+          err_attr_spec->chk(in_update_stmt);
           // create qualifier - err.attr.spec. pairs
           for (size_t qi=0; qi<nof_qualifiers; qi++) {
             if (refd_type_array[qi] && (err_attr_spec->get_indicator()!=ErroneousAttributeSpec::I_INVALID)) {
@@ -3168,6 +3172,7 @@ namespace Ttcn {
       }
     }
     if (erroneous_attrs) erroneous_attrs->chk();
+    return erroneous_attrs;
   }
 
   char* Definition::generate_code_str(char *str)
@@ -3546,8 +3551,9 @@ namespace Ttcn {
       type->chk_this_value(value, 0, Type::EXPECTED_CONSTANT, INCOMPLETE_ALLOWED,
         OMIT_NOT_ALLOWED, SUB_CHK, has_implicit_omit_attr());
       value_under_check = false;
-      chk_erroneous_attr();
-      if (erroneous_attrs) value->set_err_descr(erroneous_attrs->get_err_descr());
+      erroneous_attrs = chk_erroneous_attr(w_attrib_path, type, get_my_scope(),
+        get_fullname(), false);
+      if (erroneous_attrs) value->add_err_descr(NULL, erroneous_attrs->get_err_descr());
     {
       ReferenceChain refch(type, "While checking embedded recursions");
       value->chk_recursions(refch);
@@ -3878,7 +3884,7 @@ namespace Ttcn {
     Code::init_cdef(&cdef);
     const string& t_genname = get_genname();
     const char *name = t_genname.c_str();
-    type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", false);
+    type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", false, false);
     if (def_value) {
       cdef.init = update_location_object(cdef.init);
       cdef.init = def_value->generate_code_init(cdef.init, def_value->get_lhs_name().c_str());
@@ -4053,7 +4059,7 @@ namespace Ttcn {
     Code::init_cdef(&cdef);
     const string& t_genname = get_genname();
     const char *name = t_genname.c_str();
-    type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", true);
+    type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", true, false);
     if (def_template) {
       cdef.init = update_location_object(cdef.init);
       cdef.init = def_template->generate_code_init(cdef.init, def_template->get_lhs_name().c_str());
@@ -4233,8 +4239,9 @@ namespace Ttcn {
       ANY_OR_OMIT_ALLOWED, SUB_CHK,
       has_implicit_omit_attr() ? IMPLICIT_OMIT : NOT_IMPLICIT_OMIT, 0);
 
-    chk_erroneous_attr();
-    if (erroneous_attrs) body->set_err_descr(erroneous_attrs->get_err_descr());
+    erroneous_attrs = chk_erroneous_attr(w_attrib_path, type, get_my_scope(),
+      get_fullname(), false);
+    if (erroneous_attrs) body->add_err_descr(NULL, erroneous_attrs->get_err_descr());
 
     {
       ReferenceChain refch(type, "While checking embedded recursions");
@@ -4465,6 +4472,10 @@ namespace Ttcn {
   void Def_Template::generate_code(output_struct *target, bool)
   {
     type->generate_code(target);
+    if (body->get_err_descr() != NULL && body->get_err_descr()->has_descr(NULL)) {
+        target->functions.post_init = body->get_err_descr()->generate_code_init_str(
+          NULL, target->functions.post_init, body->get_lhs_name());
+    }
     if (fp_list) {
       // Parameterized template. Generate code for a function which returns
       // a $(genname)_template and has the appropriate parameters.
@@ -4504,10 +4515,6 @@ namespace Ttcn {
         function_body = mputprintf(function_body, "%s ret_val;\n",
           type_genname_str);
       }
-      if (erroneous_attrs && erroneous_attrs->get_err_descr()) {
-        function_body = erroneous_attrs->get_err_descr()->
-          generate_code_str(function_body, target->header.global_vars, string("ret_val"), true);
-      }
       function_body = body->generate_code_init(function_body, "ret_val");
       if (template_restriction!=TR_NONE && gen_restriction_check)
         function_body = Template::generate_restriction_check_code(function_body,
@@ -4517,6 +4524,28 @@ namespace Ttcn {
           "ttcn3_debugger.set_return_value((TTCN_Logger::begin_event_log2str(), "
           "ret_val.log(), TTCN_Logger::end_event_log2str()));\n");
       }
+      if (ErroneousDescriptors::can_have_err_attribs(type)) {
+        // these are always generated, not just if the template has erroneous
+        // descriptors, so adding '@update' statements in other modules does not
+        // require this module's code to be regenerated
+        target->source.global_vars = mputprintf(target->source.global_vars,
+          "Erroneous_descriptor_t* %s_err_descr_ptr = NULL;\n",
+          body->get_lhs_name().c_str());
+        target->header.global_vars = mputprintf(target->header.global_vars,
+          "extern Erroneous_descriptor_t* %s_err_descr_ptr;\n",
+          body->get_lhs_name().c_str());
+        function_body = mputprintf(function_body,
+          "ret_val.set_err_descr(%s_err_descr_ptr);\n",
+          body->get_lhs_name().c_str());
+      }
+      if (body->get_err_descr() != NULL && body->get_err_descr()->has_descr(NULL)) {
+        target->source.global_vars = body->get_err_descr()->generate_code_str(NULL,
+          target->source.global_vars, target->header.global_vars, body->get_lhs_name());
+        target->functions.post_init = mputprintf(target->functions.post_init,
+          "%s_err_descr_ptr = &%s_%lu_err_descr;\n",
+          body->get_lhs_name().c_str(), body->get_lhs_name().c_str(),
+          (unsigned long) body->get_err_descr()->get_descr_index(NULL));
+      }
       function_body = mputstr(function_body, "return ret_val;\n");
       // if the template modifies a parameterized template, then the inherited
       // formal parameters must always be displayed, otherwise generate a smart
@@ -4585,6 +4614,11 @@ namespace Ttcn {
       if (template_restriction != TR_NONE && gen_restriction_check)
         cdef.init = Template::generate_restriction_check_code(cdef.init,
           body->get_lhs_name().c_str(), template_restriction);
+      if (body->get_err_descr() != NULL && body->get_err_descr()->has_descr(NULL)) {
+        cdef.init = mputprintf(cdef.init, "%s.set_err_descr(&%s_%lu_err_descr);\n",
+          body->get_lhs_name().c_str(), body->get_lhs_name().c_str(),
+          (unsigned long) body->get_err_descr()->get_descr_index(NULL));
+      }
       target->header.global_vars = mputstr(target->header.global_vars,
         cdef.decl);
       target->source.global_vars = mputstr(target->source.global_vars,
@@ -4849,7 +4883,7 @@ namespace Ttcn {
     type->generate_code(target);
     const_def cdef;
     Code::init_cdef(&cdef);
-    type->generate_code_object(&cdef, my_scope, get_genname(), 0, false);
+    type->generate_code_object(&cdef, my_scope, get_genname(), 0, false, false);
     Code::merge_cdef(target, &cdef);
     Code::free_cdef(&cdef);
     if (initial_value) {
@@ -5062,7 +5096,7 @@ namespace Ttcn {
     type->generate_code(target);
     const_def cdef;
     Code::init_cdef(&cdef);
-    type->generate_code_object(&cdef, my_scope, get_genname(), 0, true);
+    type->generate_code_object(&cdef, my_scope, get_genname(), 0, true, false);
     Code::merge_cdef(target, &cdef);
     Code::free_cdef(&cdef);
     if (initial_value) {
@@ -6344,7 +6378,8 @@ namespace Ttcn {
     if (debugger_active) {
       body = generate_code_debugger_function_init(body, this);
     }
-    body = block->generate_code(body);
+    body = block->generate_code(body, target->header.global_vars,
+      target->source.global_vars);
     // smart formal parameter list (names of unused parameters are omitted)
     char *formal_par_list = fp_list->generate_code(memptystr());
     fp_list->generate_code_defval(target);
@@ -7488,8 +7523,10 @@ namespace Ttcn {
     if (debugger_active) {
       body = generate_code_debugger_function_init(body, this);
     }
-    body = sb->generate_code(body);
-    body = ags->generate_code_altstep(body);
+    body = sb->generate_code(body, target->header.global_vars,
+      target->source.global_vars);
+    body = ags->generate_code_altstep(body, target->header.global_vars,
+      target->source.global_vars);
     // generate a smart formal parameter list (omits unused parameter names)
     char *formal_par_list = fp_list->generate_code(memptystr());
     fp_list->generate_code_defval(target);
@@ -7772,7 +7809,8 @@ namespace Ttcn {
       body = system_type->get_CompBody()->generate_code_comptype_name(body);
     else body = runs_on_body->generate_code_comptype_name(body);
     body = mputstr(body, ", has_timer, timer_value);\n");
-    body = block->generate_code(body);
+    body = block->generate_code(body, target->header.global_vars,
+      target->source.global_vars);
     body = mputprintf(body,
       "} catch (const TC_Error& tc_error) {\n"
       "} catch (const TC_End& tc_end) {\n"
diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh
index ff46e62b1..de7ac9ffa 100644
--- a/compiler2/ttcn3/AST_ttcn3.hh
+++ b/compiler2/ttcn3/AST_ttcn3.hh
@@ -888,8 +888,9 @@ namespace Ttcn {
      */
     virtual bool chk_identical(Definition *p_def);
     /** Parse and check the erroneous attribute data,
-      * sets erroneous_attrs member */
-    void chk_erroneous_attr();
+      * returns erroneous attributes or NULL */
+    static ErroneousAttributes* chk_erroneous_attr(WithAttribPath* p_attrib_path,
+      Type* p_type, Scope* p_scope, string p_fullname, bool in_update_stmt);
     /** This code generation is used when this definition is embedded
      * in a statement block. */
     virtual char* generate_code_str(char *str);
diff --git a/compiler2/ttcn3/Attributes.cc b/compiler2/ttcn3/Attributes.cc
index 042096ef2..c291857c4 100644
--- a/compiler2/ttcn3/Attributes.cc
+++ b/compiler2/ttcn3/Attributes.cc
@@ -23,6 +23,7 @@
 #include "../Type.hh"
 #include "../main.hh"
 #include "TtcnTemplate.hh"
+#include "Statement.hh"
 
 namespace Ttcn {
 
@@ -226,7 +227,7 @@ namespace Ttcn {
     return (tmpl_inst->get_Template()->get_templatetype()==Template::OMIT_VALUE);
   }
 
-  void ErroneousAttributeSpec::chk()
+  void ErroneousAttributeSpec::chk(bool in_update_stmt)
   {
     if (get_is_omit()) { // special case, no type needed
       if ((indicator==I_BEFORE)||(indicator==I_AFTER)) {
@@ -290,7 +291,10 @@ namespace Ttcn {
       value = templ->get_Value();
       value->set_my_governor(type);
       type->chk_this_value_ref(value);
-      type->chk_this_value(value, 0, Type::EXPECTED_CONSTANT,
+      // dynamic values are allowed for attribute specs in '@update' statements,
+      // but not for the initial attribute specs of constants and templates
+      type->chk_this_value(value, 0, in_update_stmt ?
+        Type::EXPECTED_DYNAMIC_VALUE : Type::EXPECTED_CONSTANT,
         INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK);
       //{ FIXME: make this work
       //  ReferenceChain refch(type, "While checking embedded recursions");
@@ -315,21 +319,21 @@ namespace Ttcn {
     return "";
   }
 
-  char* ErroneousAttributeSpec::generate_code_str(char *str, char *& def, string genname, const bool embedded)
+  char* ErroneousAttributeSpec::generate_code_str(char *str, char *& def, string genname)
   {
     if (get_is_omit()) return str;
     if (!type) FATAL_ERROR("ErroneousAttributeSpec::generate_code_str()");
     if (!value) FATAL_ERROR("ErroneousAttributeSpec::generate_code_str()");
     if (first_genname.empty()) { // this is the first use
-      str = mputprintf(str, "%s%s %s;\n", split_to_slices && !embedded ? "" : "static ",
+      str = mputprintf(str, "%s%s %s;\n", split_to_slices ? "" : "static ",
         type->get_genname_value(value->get_my_scope()).c_str(), genname.c_str());
       first_genname = genname;
-      if (split_to_slices && !embedded) {
+      if (split_to_slices) {
         def = mputprintf(def, "extern %s %s;\n",
           type->get_genname_value(value->get_my_scope()).c_str(), genname.c_str());
       }
     } else {
-      str = mputprintf(str, "%s%s& %s = %s;\n", split_to_slices && !embedded ? "" : "static ",
+      str = mputprintf(str, "%s& %s = %s;\n",
         type->get_genname_value(value->get_my_scope()).c_str(),
         genname.c_str(), first_genname.c_str());
     }
@@ -364,22 +368,23 @@ namespace Ttcn {
 
   // ==== ErroneousValues ====
 
-  char* ErroneousValues::generate_code_embedded_str(char *str, char *& def, string genname, const bool embedded)
+  char* ErroneousValues::generate_code_embedded_str(char *str, char *& def, string genname)
   {
-    if (before) str = generate_code_embedded_str(str, def, genname+"_before", before, embedded);
-    if (value) str = generate_code_embedded_str(str, def, genname+"_value", value, embedded);
-    if (after) str = generate_code_embedded_str(str, def, genname+"_after", after, embedded);
+    if (before) str = generate_code_embedded_str(str, def, genname+"_before", before);
+    if (value) str = generate_code_embedded_str(str, def, genname+"_value", value);
+    if (after) str = generate_code_embedded_str(str, def, genname+"_after", after);
     return str;
   }
 
-  char* ErroneousValues::generate_code_embedded_str(char *str, char *& def, string genname, ErroneousAttributeSpec* attr_spec, const bool embedded)
+  char* ErroneousValues::generate_code_embedded_str(char *str, char *& def, string genname, ErroneousAttributeSpec* attr_spec)
   {
-    str = attr_spec->generate_code_str(str, def, genname+"_errval", embedded);
-    str = mputprintf(str, "%sErroneous_value_t %s = { %s, %s, %s };\n", split_to_slices && !embedded ? "" : "static ", genname.c_str(),
+    str = attr_spec->generate_code_str(str, def, genname+"_errval");
+    str = mputprintf(str, "%sErroneous_value_t %s = { %s, %s, %s };\n",
+      split_to_slices ? "" : "static ", genname.c_str(),
       attr_spec->get_is_raw() ? "true" : "false",
       attr_spec->get_is_omit() ? "NULL" : ("&"+genname+"_errval").c_str(),
       attr_spec->get_typedescriptor_str().c_str());
-    if (split_to_slices && !embedded) {
+    if (split_to_slices) {
       def = mputprintf(def, "extern Erroneous_value_t %s;\n", genname.c_str());
     }
     return str;
@@ -412,39 +417,41 @@ namespace Ttcn {
 
   // ==== ErroneousDescriptor ====
 
-  char* ErroneousDescriptor::generate_code_embedded_str(char *str, char *& def, string genname, const bool embedded)
+  char* ErroneousDescriptor::generate_code_embedded_str(char *str, char *& def, string genname)
   {
     // values
     for (size_t i=0; i<values_m.size(); i++) {
-      str = values_m.get_nth_elem(i)->generate_code_embedded_str(str, def, genname+"_v"+Int2string((int)values_m.get_nth_key(i)), embedded);
+      str = values_m.get_nth_elem(i)->generate_code_embedded_str(str, def, genname+"_v"+Int2string((int)values_m.get_nth_key(i)));
     }
     // embedded descriptors
     for (size_t i=0; i<descr_m.size(); i++) {
-      str = descr_m.get_nth_elem(i)->generate_code_embedded_str(str, def, genname+"_d"+Int2string((int)descr_m.get_nth_key(i)), embedded);
+      str = descr_m.get_nth_elem(i)->generate_code_embedded_str(str, def, genname+"_d"+Int2string((int)descr_m.get_nth_key(i)));
     }
     // values vector
     if (values_m.size()>0) {
-      str = mputprintf(str, "%sErroneous_values_t %s_valsvec[%d] = { ", split_to_slices && !embedded ? "" : "static ", genname.c_str(), (int)values_m.size());
+      str = mputprintf(str, "%sErroneous_values_t %s_valsvec[%d] = { ",
+        split_to_slices ? "" : "static ", genname.c_str(), (int)values_m.size());
       for (size_t i=0; i<values_m.size(); i++) {
         if (i>0) str = mputstr(str, ", ");
         int key_i = (int)values_m.get_nth_key(i);
         str = values_m.get_nth_elem(i)->generate_code_struct_str(str, genname+"_v"+Int2string(key_i), key_i);
       }
       str = mputstr(str, " };\n");
-      if (split_to_slices && !embedded) {
+      if (split_to_slices) {
         def = mputprintf(def, "extern Erroneous_values_t %s_valsvec[%d];\n", genname.c_str(), (int)values_m.size());
       }
     }
     // embedded descriptor vector
     if (descr_m.size()>0) {
-      str = mputprintf(str, "%sErroneous_descriptor_t %s_embvec[%d] = { ", split_to_slices && !embedded ? "" : "static ", genname.c_str(), (int)descr_m.size());
+      str = mputprintf(str, "%sErroneous_descriptor_t %s_embvec[%d] = { ",
+        split_to_slices ? "" : "static ", genname.c_str(), (int)descr_m.size());
       for (size_t i=0; i<descr_m.size(); i++) {
         if (i>0) str = mputstr(str, ", ");
         int key_i = (int)descr_m.get_nth_key(i);
         str = descr_m.get_nth_elem(i)->generate_code_struct_str(str, def, genname+"_d"+Int2string(key_i), key_i);
       }
       str = mputstr(str, " };\n");
-      if (split_to_slices && !embedded) {
+      if (split_to_slices) {
         def = mputprintf(def, "extern Erroneous_descriptor_t %s_embvec[%d];\n", genname.c_str(), (int)descr_m.size());
       }
     }
@@ -486,18 +493,83 @@ namespace Ttcn {
     }
   }
 
-  char* ErroneousDescriptor::generate_code_str(char *str, char *& def, string genname, const bool embedded)
+  char* ErroneousDescriptor::generate_code_str(char *str, char *& def, string genname)
   {
     genname += "_err_descr";
-    str = generate_code_embedded_str(str, def, genname, embedded);
-    str = mputprintf(str, "%sErroneous_descriptor_t %s = ", split_to_slices && !embedded ? "" : "static ", genname.c_str());
+    str = generate_code_embedded_str(str, def, genname);
+    str = mputprintf(str, "%sErroneous_descriptor_t %s = ",
+      split_to_slices ? "" : "static ", genname.c_str());
     str = generate_code_struct_str(str, def, genname, -1);
     str = mputstr(str, ";\n");
-    if (split_to_slices && !embedded) {
+    if (split_to_slices) {
       def = mputprintf(def, "extern Erroneous_descriptor_t %s;\n", genname.c_str());
     }
     return str;
   }
+  
+  ErroneousDescriptors::~ErroneousDescriptors()
+  {
+    descr_map.clear();
+  }
+  
+  void ErroneousDescriptors::add(Statement* p_update_statement, ErroneousDescriptor* p_descr)
+  {
+    descr_map.add(p_update_statement, p_descr);
+  }
+  
+  bool ErroneousDescriptors::has_descr(Statement* p_update_statement)
+  {
+    return descr_map.has_key(p_update_statement);
+  }
+  
+  size_t ErroneousDescriptors::get_descr_index(Statement* p_update_statement)
+  {
+    return descr_map.find_key(p_update_statement);
+  }
+  
+  char* ErroneousDescriptors::generate_code_init_str(Statement* p_update_statement, char *str, string genname)
+  {
+    size_t i = descr_map.find_key(p_update_statement);
+    return descr_map.get_nth_elem(i)->generate_code_init_str(str,
+      genname + string("_") + Int2string((int)i) + string("_err_descr"));
+  }
+  
+  char* ErroneousDescriptors::generate_code_str(Statement* p_update_statement, char *str, char *& def, string genname)
+  {
+    size_t i = descr_map.find_key(p_update_statement);
+    return descr_map.get_nth_elem(i)->generate_code_str(str, def,
+      genname + string("_") + Int2string((int)i));
+  }
+  
+  void ErroneousDescriptors::chk_recursions(ReferenceChain& refch)
+  {
+    for (size_t i = 0; i < descr_map.size(); ++i) {
+      descr_map.get_nth_elem(i)->chk_recursions(refch);
+    }
+  }
+  
+  boolean ErroneousDescriptors::can_have_err_attribs(Type* t)
+  {
+    if (t == NULL) {
+      FATAL_ERROR("ErroneousDescriptors::can_have_err_attribs");
+    }
+    t = t->get_type_refd_last();
+    switch (t->get_typetype_ttcn3()) {
+    case Type::T_SEQ_T:
+    case Type::T_SET_T:
+      if (t->get_nof_comps() == 0) {
+        // empty records/sets can't have erroneous attributes
+        return FALSE;
+      }
+      // else fall through
+    case Type::T_SEQOF:
+    case Type::T_SETOF:
+    case Type::T_CHOICE_T:
+      return use_runtime_2;
+    default:
+      return FALSE;
+    }
+  }
 
   // ==== ErroneousAttributes ====
 
@@ -1016,6 +1088,23 @@ namespace Ttcn {
 
     attributes_checked = true;
   }
+  
+  void WithAttribPath::chk_only_erroneous()
+  {
+    if (attributes_checked || m_w_attrib == NULL) {
+      return;
+    }
+
+    for (size_t i = 0; i < m_w_attrib->get_nof_elements(); ++i) {
+      const SingleWithAttrib* attrib = m_w_attrib->get_element(i);
+      if (attrib->get_attribKeyword() != SingleWithAttrib::AT_ERRONEOUS) {
+        attrib->error("Only `erroneous' attributes are allowed in an `@update' "
+          "statement");
+      }
+    }
+
+    attributes_checked = true;
+  }
 
   void WithAttribPath::dump(unsigned int level) const
   {
diff --git a/compiler2/ttcn3/Attributes.hh b/compiler2/ttcn3/Attributes.hh
index 41e4faa9b..7b4048f22 100644
--- a/compiler2/ttcn3/Attributes.hh
+++ b/compiler2/ttcn3/Attributes.hh
@@ -33,6 +33,7 @@ namespace Ttcn {
   class Group;
   class Def_Template;
   class TemplateInstance;
+  class Statement;
 
   /** Attribute qualifier (DefOrFieldRef). */
   class Qualifier: public FieldOrArrayRefs, public Location
@@ -113,13 +114,13 @@ namespace Ttcn {
     void set_my_scope(Scope *p_scope);
     void dump(unsigned level) const;
     /** basic check, the qualifier of the field is not known here */
-    void chk();
+    void chk(bool in_update_stmt);
     indicator_t get_indicator() const { return indicator; }
     Type* get_type() const { return type; }
     bool get_is_raw() const { return is_raw; }
     bool get_is_omit() const;
     static const char* get_indicator_str(indicator_t i);
-    char* generate_code_str(char *str, char *& def, string genname, const bool embedded);
+    char* generate_code_str(char *str, char *& def, string genname);
     char* generate_code_init_str(char *str, string genname);
     string get_typedescriptor_str();
     void chk_recursions(ReferenceChain& refch);
@@ -132,9 +133,9 @@ namespace Ttcn {
     ErroneousAttributeSpec *before, *value, *after; // NULL if not specified
     string field_name; // qualifier string
     ErroneousValues(const string& p_field_name): before(0), value(0), after(0), field_name(p_field_name) {}
-    char* generate_code_embedded_str(char *str, char *& def, string genname, const bool embedded);
+    char* generate_code_embedded_str(char *str, char *& def, string genname);
     char* generate_code_init_str(char *str, string genname);
-    char* generate_code_embedded_str(char *str, char *& def, string genname, ErroneousAttributeSpec* attr_spec, const bool embedded);
+    char* generate_code_embedded_str(char *str, char *& def, string genname, ErroneousAttributeSpec* attr_spec);
     char* generate_code_struct_str(char *str, string genname, int field_index);
     void chk_recursions(ReferenceChain& refch);
   };
@@ -152,11 +153,36 @@ namespace Ttcn {
   public:
     ErroneousDescriptor(): omit_before(-1), omit_after(-1) {}
     ~ErroneousDescriptor();
-    char* generate_code_embedded_str(char *str, char *& def, string genname, const bool embedded);
+    char* generate_code_embedded_str(char *str, char *& def, string genname);
     char* generate_code_init_str(char *str, string genname);
     char* generate_code_struct_str(char *str, char *& def, string genname, int field_index);
-    char* generate_code_str(char *str, char *& def, string genname, const bool embedded);
+    char* generate_code_str(char *str, char *& def, string genname);
+    void chk_recursions(ReferenceChain& refch);
+  };
+  
+  /**
+   * helper to construct several trees of erroneous attributes
+   * (contains the set of erroneous attributes set at initialization and the
+   * sets of erroneous attributes set by '@update' statements)
+   */
+  class ErroneousDescriptors {
+    /** Map of erroneous descriptors 
+      * Key: pointer to the '@update' statement or NULL (for the erroneous
+      * attributes specified at initialization)
+      * Values not owned */
+    map<Statement*, ErroneousDescriptor> descr_map;
+    ErroneousDescriptors(const ErroneousDescriptor& p); // disabled
+    ErroneousDescriptors& operator=(const ErroneousDescriptors& p); // disabled
+  public:
+    ErroneousDescriptors() {}
+    ~ErroneousDescriptors();
+    void add(Statement* p_update_statement, ErroneousDescriptor* p_descr);
+    bool has_descr(Statement* p_update_statement);
+    size_t get_descr_index(Statement* p_update_statement);
+    char* generate_code_init_str(Statement* p_update_statement, char *str, string genname);
+    char* generate_code_str(Statement* p_update_statement, char *str, char *& def, string genname);
     void chk_recursions(ReferenceChain& refch);
+    static boolean can_have_err_attribs(Type* t);
   };
 
   /**
@@ -319,6 +345,7 @@ namespace Ttcn {
     void set_had_global_variants(bool has) { had_global_variants = has; }
     bool get_had_global_variants() { return had_global_variants; }
     void chk_no_qualif();
+    void chk_only_erroneous();
     void chk_global_attrib(bool erroneous_allowed=false);
     void set_parent(WithAttribPath* p_parent) { parent = p_parent; }
     WithAttribPath* get_parent() { return parent; }
diff --git a/compiler2/ttcn3/ILT.cc b/compiler2/ttcn3/ILT.cc
index a80f004b1..edd9c8186 100644
--- a/compiler2/ttcn3/ILT.cc
+++ b/compiler2/ttcn3/ILT.cc
@@ -48,8 +48,9 @@ namespace Ttcn {
   // ===== ILT_root
   // =================================
 
-  ILT_root::ILT_root(Statement *p_il)
-    : il(p_il), tmpnum(0), c_l(0), c_b(0)
+  ILT_root::ILT_root(Statement *p_il, char*& p_def_glob_vars, char*& p_src_glob_vars)
+    : il(p_il), tmpnum(0), c_l(0), c_b(0), out_def_glob_vars(p_def_glob_vars)
+    , out_src_glob_vars(p_src_glob_vars)
   {
     if(!p_il || p_il->get_statementtype()!=Statement::S_INTERLEAVE)
       FATAL_ERROR("ILT_root::ILT_root()");
@@ -100,6 +101,16 @@ namespace Ttcn {
   {
     return out_branches;
   }
+  
+  char*& ILT_root::get_out_def_glob_vars()
+  {
+    return out_def_glob_vars;
+  }
+  
+  char*& ILT_root::get_out_src_glob_vars()
+  {
+    return out_src_glob_vars;
+  }
 
   const string& ILT_root::get_my_tmpid()
   {
@@ -293,6 +304,16 @@ namespace Ttcn {
   {
     return root->get_out_branches();
   }
+  
+  char*& ILT_branch::get_out_def_glob_vars()
+  {
+    return root->get_out_def_glob_vars();
+  }
+  
+  char*& ILT_branch::get_out_src_glob_vars()
+  {
+    return root->get_out_src_glob_vars();
+  }
 
   const string& ILT_branch::get_my_tmpid()
   {
@@ -411,7 +432,8 @@ namespace Ttcn {
       }
       else {
         out_stmt=mputstr(out_stmt, "{\n"); // (2)
-        out_stmt=block->generate_code(out_stmt);
+        out_stmt=block->generate_code(out_stmt, get_out_def_glob_vars(),
+          get_out_src_glob_vars());
       }
       if(branchtype==BT_IL) {
         out_stmt=mputprintf(out_stmt, "%s_state[%lu]=1;\n",
diff --git a/compiler2/ttcn3/ILT.hh b/compiler2/ttcn3/ILT.hh
index fd9394253..5957cb45f 100644
--- a/compiler2/ttcn3/ILT.hh
+++ b/compiler2/ttcn3/ILT.hh
@@ -52,6 +52,8 @@ namespace Ttcn {
     virtual char*& get_out_def() = 0;
     virtual char*& get_out_code() = 0;
     virtual char*& get_out_branches() = 0;
+    virtual char*& get_out_def_glob_vars() = 0;
+    virtual char*& get_out_src_glob_vars() = 0;
     virtual const string& get_my_tmpid() = 0;
     virtual size_t get_new_tmpnum() = 0;
     virtual size_t get_new_state_var(bool toplevel) = 0;
@@ -78,6 +80,8 @@ namespace Ttcn {
     char *out_statevars; // 0: unused; 1: ready; 2: toplevel (IL) etc
     char *out_code;
     char *out_branches;
+    char*& out_def_glob_vars;
+    char*& out_src_glob_vars;
   private:
     /** Copy constructor not implemented */
     ILT_root(const ILT_root& p);
@@ -85,7 +89,7 @@ namespace Ttcn {
     ILT_root& operator=(const ILT_root& p);
   public:
     /** Constructor; the parameter should be an InterleavedStatement */
-    ILT_root(Statement *p_il);
+    ILT_root(Statement *p_il, char*& p_def_glob_vars, char*& p_src_glob_vars);
     virtual ~ILT_root();
     virtual ILT_root *clone() const;
     virtual ILT_root* get_my_root();
@@ -93,6 +97,8 @@ namespace Ttcn {
     virtual char*& get_out_def();
     virtual char*& get_out_code();
     virtual char*& get_out_branches();
+    virtual char*& get_out_def_glob_vars();
+    virtual char*& get_out_src_glob_vars();
     virtual const string& get_my_tmpid();
     virtual size_t get_new_tmpnum();
     virtual size_t get_new_state_var(bool toplevel);
@@ -151,6 +157,8 @@ namespace Ttcn {
     virtual char*& get_out_def();
     virtual char*& get_out_code();
     virtual char*& get_out_branches();
+    virtual char*& get_out_def_glob_vars();
+    virtual char*& get_out_src_glob_vars();
     virtual const string& get_my_tmpid();
     virtual size_t get_new_tmpnum();
     virtual size_t get_new_state_var(bool toplevel);
diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc
index a8dd88b60..f972355ed 100644
--- a/compiler2/ttcn3/Statement.cc
+++ b/compiler2/ttcn3/Statement.cc
@@ -420,14 +420,14 @@ namespace Ttcn {
       stmts[i]->set_code_section(p_code_section);
   }
 
-  char* StatementBlock::generate_code(char *str)
+  char* StatementBlock::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars)
   {
     if (exception_handling==EH_TRY) {
       str = mputstr(str, "TTCN_TryBlock try_block;\n");
     }
     if (stmts.size()>0) {
       Statement* first_stmt = stmts[0];
-      str = first_stmt->generate_code(str);
+      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();
@@ -436,7 +436,7 @@ namespace Ttcn {
       }
     }
     for(size_t i=1; i<stmts.size(); i++) {
-      str = stmts[i]->generate_code(str);
+      str = stmts[i]->generate_code(str, def_glob_vars, src_glob_vars);
     }
     return str;
   }
@@ -452,7 +452,8 @@ namespace Ttcn {
       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);
+        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;
     }
@@ -463,7 +464,8 @@ namespace Ttcn {
     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);
+      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");
   }
 
@@ -711,6 +713,11 @@ namespace Ttcn {
       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;
     default:
       FATAL_ERROR("Statement::clean_up()");
     } // switch statementtype
@@ -1434,6 +1441,29 @@ namespace Ttcn {
       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()
   {
@@ -1563,6 +1593,7 @@ namespace Ttcn {
     case S_INT2ENUM: return "int2enum";
     case S_START_PROFILER: return "@profiler.start";
     case S_STOP_PROFILER: return "@profiler.stop";
+    case S_UPDATE: return "@update";
     default:
       FATAL_ERROR("Statement::get_stmt_name()");
       return "";
@@ -1886,6 +1917,12 @@ namespace Ttcn {
       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;
     default:
       FATAL_ERROR("Statement::set_my_scope()");
     } // switch statementtype
@@ -2164,6 +2201,12 @@ namespace Ttcn {
       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;
     default:
       FATAL_ERROR("Statement::set_fullname()");
     } // switch statementtype
@@ -2453,6 +2496,7 @@ namespace Ttcn {
     case S_START_PROFILER:
     case S_STOP_PROFILER:
     case S_INT2ENUM:
+    case S_UPDATE:
       return false;
     case S_ALT:
     case S_INTERLEAVE:
@@ -2707,6 +2751,9 @@ namespace Ttcn {
     case S_STOP_PROFILER:
       // do nothing
       break;
+    case S_UPDATE:
+      chk_update();
+      break;
     default:
       FATAL_ERROR("Statement::chk()");
     } // switch statementtype
@@ -5531,6 +5578,49 @@ error:
       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;
+    }
+    Common::Assignment* refd_ass = update_op.ref->get_refd_assignment(false);
+    Type* ref_type = NULL;
+    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;
+      }
+      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::set_code_section(
     GovernedSimple::code_section_t p_code_section)
@@ -5786,12 +5876,15 @@ error:
       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;
     default:
       FATAL_ERROR("Statement::set_code_section()");
     } // switch statementtype
   }
 
-  char *Statement::generate_code(char *str)
+  char *Statement::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars)
   {
     switch (statementtype) {
     case S_BLOCK:
@@ -5824,7 +5917,7 @@ error:
       str=generate_code_invoke(str);
       break;
     case S_BLOCK:
-      str=generate_code_block(str);
+      str=generate_code_block(str, def_glob_vars, src_glob_vars);
       break;
     case S_LOG:
       str=generate_code_log(str);
@@ -5836,22 +5929,22 @@ error:
       str = generate_code_goto(str);
       break;
     case S_IF:
-      str=generate_code_if(str);
+      str=generate_code_if(str, def_glob_vars, src_glob_vars);
       break;
     case S_SELECT:
-      str=generate_code_select(str);
+      str=generate_code_select(str, def_glob_vars, src_glob_vars);
       break;
     case S_SELECTUNION:
-      str=generate_code_select_union(str);
+      str=generate_code_select_union(str, def_glob_vars, src_glob_vars);
       break;
     case S_FOR:
-      str=generate_code_for(str);
+      str=generate_code_for(str, def_glob_vars, src_glob_vars);
       break;
     case S_WHILE:
-      str=generate_code_while(str);
+      str=generate_code_while(str, def_glob_vars, src_glob_vars);
       break;
     case S_DOWHILE:
-      str=generate_code_dowhile(str);
+      str=generate_code_dowhile(str, def_glob_vars, src_glob_vars);
       break;
     case S_BREAK:
       str=generate_code_break(str);
@@ -5866,13 +5959,13 @@ error:
       str=generate_code_testcase_stop(str);
       break;
     case S_ALT:
-      str=ags->generate_code_alt(str, *this);
+      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);
+      str=generate_code_interleave(str, def_glob_vars, src_glob_vars);
       break;
     case S_RETURN:
       str=generate_code_return(str);
@@ -5890,7 +5983,7 @@ error:
       str = generate_code_send(str);
       break;
     case S_CALL:
-      str = generate_code_call(str);
+      str = generate_code_call(str, def_glob_vars, src_glob_vars);
       break;
     case S_REPLY:
       str = generate_code_reply(str);
@@ -5979,6 +6072,9 @@ error:
     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;
     default:
       FATAL_ERROR("Statement::generate_code()");
     } // switch
@@ -6120,7 +6216,7 @@ error:
     }
     if (!has_receiving_stmt()) {
       char*& str=ilt->get_out_branches();
-      str=generate_code(str);
+      str=generate_code(str, ilt->get_out_def_glob_vars(), ilt->get_out_src_glob_vars());
       return;
     }
     switch (statementtype) {
@@ -6235,7 +6331,7 @@ error:
     return str;
   }
 
-  char *Statement::generate_code_block(char *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:
@@ -6254,7 +6350,7 @@ error:
       if (debugger_active) {
         str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
       }
-      str = block->generate_code(str);
+      str = block->generate_code(str, def_glob_vars, src_glob_vars);
       str = mputstr(str, "}\n");
     } else str = mputstr(str, "/* empty block */;\n");
     return str;
@@ -6331,11 +6427,12 @@ error:
     return str;
   }
 
-  char* Statement::generate_code_if(char *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, blockcount, unreach, eachfalse);
+    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;
@@ -6344,14 +6441,14 @@ error:
         str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
       }
       blockcount++;
-      str=if_stmt.elseblock->generate_code(str);
+      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* 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();
@@ -6377,10 +6474,10 @@ error:
       gen_switch_code = select.scs->can_generate_switch();
     }
     if (gen_switch_code) {
-      str=select.scs->generate_code_switch(str, tmp_id.c_str());
+      str=select.scs->generate_code_switch(str, def_glob_vars, src_glob_vars, tmp_id.c_str());
     }
     else {
-      str=select.scs->generate_code(str, tmp_prefix.c_str(), tmp_id.c_str());
+      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");
@@ -6388,7 +6485,7 @@ error:
     return str;
   }
   
-  char* Statement::generate_code_select_union(char *str)
+  char* Statement::generate_code_select_union(char *str, char*& def_glob_vars, char*& src_glob_vars)
   {
     expression_struct expr;
     Code::init_expr(&expr);
@@ -6400,7 +6497,7 @@ error:
     const char* type_name = select_union.expr->get_expr_governor_last()->get_genname_value(select_union.expr->get_my_scope()).c_str();
     char* loc = NULL;
     loc = select_union.expr->update_location_object(loc);
-    str = select_union.sus->generate_code(str, type_name, 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);
@@ -6410,7 +6507,7 @@ error:
     return str;
   }
   
-  char *Statement::generate_code_for(char *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
@@ -6451,7 +6548,7 @@ error:
       if (debugger_active) {
         str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
       }
-      str = loop.block->generate_code(str);
+      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());
@@ -6463,7 +6560,7 @@ error:
     return str;
   }
 
-  char *Statement::generate_code_while(char *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;
@@ -6490,13 +6587,13 @@ error:
       if (debugger_active) {
         str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
       }
-      str = loop.block->generate_code(str);
+      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 *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();
@@ -6510,7 +6607,7 @@ error:
       if (debugger_active) {
         str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
       }
-      str = loop.block->generate_code(str);
+      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))
@@ -6522,7 +6619,7 @@ error:
       if (debugger_active) {
         str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
       }
-      str = loop.block->generate_code(str);
+      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)
@@ -6592,9 +6689,9 @@ error:
     return str;
   }
 
-  char* Statement::generate_code_interleave(char *str)
+  char* Statement::generate_code_interleave(char *str, char*& def_glob_vars, char*& src_glob_vars)
   {
-    ILT_root ilt(this);
+    ILT_root ilt(this, def_glob_vars, src_glob_vars);
     str=ilt.generate_code(str);
     return str;
   }
@@ -6776,7 +6873,8 @@ error:
     const char* type_name = select_union.expr->get_expr_governor_last()->get_genname_value(select_union.expr->get_my_scope()).c_str();
     char* loc = NULL;
     loc = select_union.expr->update_location_object(loc);
-    str = select_union.sus->generate_code(str, type_name, 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);
@@ -6817,8 +6915,9 @@ error:
       // 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, *this, tmplabel,
-                                                       true);
+      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)
@@ -7078,7 +7177,7 @@ error:
     return Code::merge_free_expr(str, &expr);
   }
 
-  char *Statement::generate_code_call(char *str)
+  char *Statement::generate_code_call(char *str, char*& def_glob_vars, char*& src_glob_vars)
   {
     expression_struct expr;
     Code::init_expr(&expr);
@@ -7104,7 +7203,7 @@ error:
       }
       // 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, *this,
+      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");
     }
@@ -7385,6 +7484,60 @@ error:
     } 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(),
+          (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(),
+          (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;
+  }
 
   void Statement::generate_code_expr_receive(expression_struct *expr,
     const char *opname)
@@ -7963,6 +8116,7 @@ error:
         case S_START_PROFILER:
         case S_STOP_PROFILER:
         case S_INT2ENUM:
+        case S_UPDATE:
           break;
         default:
           FATAL_ERROR("Statement::set_parent_path()");
@@ -10988,8 +11142,8 @@ error:
     block->set_code_section(p_code_section);
   }
 
-  char* IfClause::generate_code(char *str, size_t& blockcount,
-                                bool& unreach, bool& eachfalse)
+  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()) {
@@ -11011,7 +11165,7 @@ error:
     if (debugger_active) {
       str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
     }
-    str=block->generate_code(str);
+    str=block->generate_code(str, def_glob_vars, src_glob_vars);
     str=mputstr(str, "}\n");
     return str;
   }
@@ -11182,12 +11336,12 @@ error:
       ics[i]->set_code_section(p_code_section);
   }
 
-  char* IfClauses::generate_code(char *str, size_t& blockcount,
-                                 bool& unreach, bool& eachfalse)
+  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, blockcount, unreach, eachfalse);
+      str=ics[i]->generate_code(str, def_glob_vars, src_glob_vars, blockcount, unreach, eachfalse);
     }
     return str;
   }
@@ -11333,8 +11487,9 @@ error:
     return str;
   }
   
-  char* SelectCase::generate_code_case(char *str, bool &else_branch,
-    vector<const Int>& used_numbers) {
+  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++) {
@@ -11360,14 +11515,15 @@ error:
     }
     if (!already_present_all) {
       str = mputstr(str, "{\n");
-      str = block->generate_code(str);
+      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, const char *tmp_prefix,
+  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;
@@ -11376,7 +11532,7 @@ error:
     if (debugger_active) {
       str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
     }
-    str=block->generate_code(str);
+    str=block->generate_code(str, def_glob_vars, src_glob_vars);
     str=mputprintf(str, "goto %s_end;\n}\n", tmp_prefix);
     return str;
   }
@@ -11391,7 +11547,8 @@ error:
     bool has_recv=block->has_receiving_stmt();
     if(!has_recv) {
       str=mputstr(str, "{\n");
-      str=block->generate_code(str);
+      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);
@@ -11534,8 +11691,8 @@ error:
       scs[i]->set_code_section(p_code_section);
   }
 
-  char* SelectCases::generate_code(char *str, const char *tmp_prefix,
-                                   const char *expr_name)
+  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++) {
@@ -11545,20 +11702,21 @@ error:
     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, tmp_prefix, i, unreach);
+      str=scs[i]->generate_code_stmt(str, def_glob_vars, src_glob_vars, tmp_prefix, i, unreach);
       if(unreach) break;
     }
     str=mputprintf(str, "%s_end: /* empty */;\n", tmp_prefix);
     return str;
   }
   
-  char* SelectCases::generate_code_switch(char *str, const char *expr_name)
+  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, else_branch, used_numbers);
+      str=scs[i]->generate_code_case(str, def_glob_vars, src_glob_vars, else_branch, used_numbers);
       if(else_branch) break;
     }
     str=mputprintf(str, "};");
@@ -11604,7 +11762,8 @@ error:
     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, else_branch, used_numbers);
+      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, "};");
@@ -11671,7 +11830,8 @@ error:
     block->set_code_section(p_code_section);
   }
 
-  char* SelectUnion::generate_code_case(char *str, const char *type_name, bool &else_branch)
+  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++) {
@@ -11684,7 +11844,7 @@ error:
     }
     
     str = mputstr(str, "{\n");
-    str = block->generate_code(str);
+    str = block->generate_code(str, def_glob_vars, src_glob_vars);
     str = mputstr(str, "break;\n}\n");
     return str;
   }
@@ -11820,7 +11980,8 @@ error:
     }
   }
 
-  char* SelectUnions::generate_code(char *str, const char *type_name, const char *loc)
+  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);
@@ -11828,7 +11989,7 @@ error:
     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, type_name, else_branch);
+      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");
@@ -12369,7 +12530,8 @@ error:
       ags[i]->set_code_section(p_code_section);
   }
 
-  char *AltGuards::generate_code_alt(char *str, const Location& loc)
+  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++) {
@@ -12429,7 +12591,7 @@ error:
 	  if (debugger_active) {
 	    str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
 	  }
-	  str = block->generate_code(str);
+	  str = block->generate_code(str, def_glob_vars, src_glob_vars);
 	  str = mputstr(str, "}\n");
 	}
 	// jump out of the infinite for() loop
@@ -12511,7 +12673,7 @@ error:
 	  if (debugger_active) {
 	    str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
 	  }
-	  str = block->generate_code(str);
+	  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");
@@ -12552,7 +12714,7 @@ error:
     return str;
   }
 
-  char *AltGuards::generate_code_altstep(char *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();
@@ -12572,7 +12734,7 @@ error:
 	  if (debugger_active) {
 	    str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
 	  }
-	  str = block->generate_code(str);
+	  str = block->generate_code(str, def_glob_vars, src_glob_vars);
 	  str = mputstr(str, "}\n");
 	}
 	if (block->has_return() != StatementBlock::RS_YES)
@@ -12647,7 +12809,7 @@ error:
 	  if (debugger_active) {
 	    str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
 	  }
-	  str = block->generate_code(str);
+	  str = block->generate_code(str, def_glob_vars, src_glob_vars);
 	  str = mputstr(str, "}\n");
 	}
 	if (!block || block->has_return() != StatementBlock::RS_YES)
@@ -12673,8 +12835,9 @@ error:
     return str;
   }
 
-  char* AltGuards::generate_code_call_body(char *str, const Location& loc,
-    const string& temp_id, bool in_interleave)
+  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);
@@ -12737,7 +12900,7 @@ error:
           }
           else {
             str = mputstr(str, "{\n"); // (3)
-            str = block->generate_code(str);
+            str = block->generate_code(str, def_glob_vars, src_glob_vars);
             str = mputprintf(str, "goto %s_end;\n"
                              "}\n", // (3)
                              label_str);
@@ -12748,7 +12911,7 @@ error:
       else {
         if (block && block->get_nof_stmts() > 0) {
           str = mputstr(str, "{\n"); // (3)
-          str = block->generate_code(str);
+          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)
diff --git a/compiler2/ttcn3/Statement.hh b/compiler2/ttcn3/Statement.hh
index 4dc6555ab..5d27cb5cf 100644
--- a/compiler2/ttcn3/Statement.hh
+++ b/compiler2/ttcn3/Statement.hh
@@ -58,6 +58,8 @@ namespace Ttcn {
   /* not defined here: */
   class ILT;
   class ILT_branch;
+  class WithAttribPath;
+  class ErroneousAttributes;
 
   /**
    * Represents a %StatementBlock.
@@ -156,7 +158,12 @@ namespace Ttcn {
     /** Sets the code section selector of all embedded values and
      *  templates to \a p_code_section. */
     void set_code_section(GovernedSimple::code_section_t p_code_section);
-    char* generate_code(char *str);
+    /** Generates code for this statement block.
+      * '@update' statements may need to generate code into the global variables
+      * sections of the module's header and source file, so pointer references to
+      * these strings are passed to every statement block that may contain an
+      * '@update' statement. */
+    char* generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars);
     void ilt_generate_code(ILT *ilt);
 
     virtual void set_parent_path(WithAttribPath* p_path);
@@ -258,7 +265,9 @@ namespace Ttcn {
       S_STOP_PROFILER,
       /* Conversion statements */
       S_STRING2TTCN, // convert_op
-      S_INT2ENUM // convert_op
+      S_INT2ENUM, // convert_op
+      /* update statement */
+      S_UPDATE // update_op
     };
 
     enum component_t {
@@ -466,6 +475,12 @@ namespace Ttcn {
         Value* val;
         Reference* ref;
       } convert_op;
+      
+      struct {
+        Reference* ref;
+        WithAttribPath* w_attrib_path;
+        ErroneousAttributes* err_attrib;
+      } update_op; /**< S_UPDATE */
     };
 
     Statement(const Statement& p); ///< copy disabled
@@ -589,6 +604,8 @@ namespace Ttcn {
                TemplateInstances *p_ap_list, Value *p_val);
     /** Constructor used by S_STRING2TTCN, S_INT2ENUM */
     Statement(statementtype_t p_st, Value* p_val, Reference* p_ref);
+    /** Constructor used by S_UPDATE */
+    Statement(statementtype_t p_st, Reference* p_ref, MultiWithAttrib* p_attrib);
     virtual ~Statement();
     virtual Statement* clone() const;
     virtual void dump(unsigned int level) const;
@@ -768,11 +785,12 @@ namespace Ttcn {
                             bool allow_system);
     void chk_string2ttcn();
     void chk_int2enum();
+    void chk_update();
   public:
     /** Sets the code section selector of all embedded values and
      *  templates to \a p_code_section. */
     void set_code_section(GovernedSimple::code_section_t p_code_section);
-    char* generate_code(char *str);
+    char* generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars);
     void generate_code_expr(expression_struct *expr);
     void ilt_generate_code(ILT *ilt);
     
@@ -792,22 +810,22 @@ namespace Ttcn {
     /** used for function and altstep instances */
     char *generate_code_funcinst(char *str);
     char *generate_code_invoke(char *str);
-    char *generate_code_block(char *str);
+    char *generate_code_block(char *str, char*& def_glob_vars, char*& src_glob_vars);
     char *generate_code_log(char *str);
     char* generate_code_string2ttcn(char *str);
     char *generate_code_testcase_stop(char *str);
     char *generate_code_label(char *str);
     char *generate_code_goto(char *str);
-    char *generate_code_if(char *str);
-    char *generate_code_select(char *str);
-    char *generate_code_select_union(char *str);
-    char *generate_code_for(char *str);
-    char *generate_code_while(char *str);
-    char *generate_code_dowhile(char *str);
+    char *generate_code_if(char *str, char*& def_glob_vars, char*& src_glob_vars);
+    char *generate_code_select(char *str, char*& def_glob_vars, char*& src_glob_vars);
+    char *generate_code_select_union(char *str, char*& def_glob_vars, char*& src_glob_vars);
+    char *generate_code_for(char *str, char*& def_glob_vars, char*& src_glob_vars);
+    char *generate_code_while(char *str, char*& def_glob_vars, char*& src_glob_vars);
+    char *generate_code_dowhile(char *str, char*& def_glob_vars, char*& src_glob_vars);
     char *generate_code_break(char *str);
     char *generate_code_continue(char *str);
     char *generate_code_repeat(char *str);
-    char *generate_code_interleave(char *str);
+    char *generate_code_interleave(char *str, char*& def_glob_vars, char*& src_glob_vars);
     void ilt_generate_code_interleave(ILT *ilt);
     void ilt_generate_code_alt(ILT *ilt);
     void ilt_generate_code_receiving(ILT *ilt);
@@ -824,7 +842,7 @@ namespace Ttcn {
     char *generate_code_activate_refd(char *str);
     char *generate_code_deactivate(char *str);
     char *generate_code_send(char *str);
-    char *generate_code_call(char *str);
+    char *generate_code_call(char *str, char*& def_glob_vars, char*& src_glob_vars);
     char *generate_code_reply(char *str);
     char *generate_code_raise(char *str);
     char *generate_code_portop(char *str, const char *opname);
@@ -838,6 +856,7 @@ namespace Ttcn {
     char *generate_code_action(char *str);
     char *generate_code_testcaseinst(char *str);
     char *generate_code_execute_refd(char *str);
+    char* generate_code_update(char *str, char*& def_glob_vars, char*& src_glob_vars);
     /** used for receive, check-receive, trigger */
     void generate_code_expr_receive(expression_struct *expr,
       const char *opname);
@@ -1292,8 +1311,8 @@ namespace Ttcn {
     /** Sets the code section selector of all embedded values and
      *  templates to \a p_code_section. */
     void set_code_section(GovernedSimple::code_section_t p_code_section);
-    char* generate_code(char *str, size_t& blockcount,
-                        bool& unreach, bool& eachfalse);
+    char* generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars,
+      size_t& blockcount, bool& unreach, bool& eachfalse);
     void ilt_generate_code(ILT *ilt, const char *end_label, bool& unreach);
 
     /** Needed by implicit omit. Pushes attrib path down to definitions
@@ -1337,8 +1356,8 @@ namespace Ttcn {
     /** Sets the code section selector of all embedded values and
      *  templates to \a p_code_section. */
     void set_code_section(GovernedSimple::code_section_t p_code_section);
-    char* generate_code(char *str, size_t& blockcount,
-                        bool& unreach, bool& eachfalse);
+    char* generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars,
+      size_t& blockcount, bool& unreach, bool& eachfalse);
     void ilt_generate_code(ILT *ilt, const char *end_label, bool& unreach);
 
     /** Needed by implicit omit. Pushes attrib path down to definitions
@@ -1374,10 +1393,10 @@ namespace Ttcn {
     void set_code_section(GovernedSimple::code_section_t p_code_section);
     char* generate_code_if(char *str, const char *tmp_prefix,
                            const char *expr_name, size_t idx, bool& unreach);
-    char* generate_code_case(char *str, bool& else_branch,
-                             vector<const Int>& used_numbers);
-    char* generate_code_stmt(char *str, const char *tmp_prefix,
-                             size_t idx, bool& unreach);
+    char* generate_code_case(char *str, char*& def_glob_vars, char*& src_glob_vars,
+      bool& else_branch, vector<const Int>& used_numbers);
+    char* generate_code_stmt(char *str, char*& def_glob_vars, char*& src_glob_vars,
+      const char *tmp_prefix, size_t idx, bool& unreach);
     void ilt_generate_code_stmt(ILT *ilt, const char *tmp_prefix,
                                 size_t idx, bool& unreach);
 
@@ -1420,14 +1439,14 @@ namespace Ttcn {
     /** Sets the code section selector of all embedded values and
      *  templates to \a p_code_section. */
     void set_code_section(GovernedSimple::code_section_t p_code_section);
-    char *generate_code(char *str, const char *tmp_prefix,
-                        const char *expr_name);   
+    char *generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars,
+      const char *tmp_prefix, const char *expr_name);   
     void ilt_generate_code(ILT *ilt, const char *tmp_prefix,
                            const char *expr_init, const char *head_expr,
                            const char *expr_name);
     /** generates code with switch c++ statement only for integer 
      *  compatible types*/
-    char *generate_code_switch(char *str, const char *expr_name);
+    char *generate_code_switch(char *str, char*& def_glob_vars, char*& src_glob_vars, const char *expr_name);
     void ilt_generate_code_switch(ILT *ilt, const char *expr_init, const char *head_expr, const char *expr_name);
     
 
@@ -1461,7 +1480,8 @@ namespace Ttcn {
     /* checking functions */
     void chk(Type *p_gov);
     void set_code_section(GovernedSimple::code_section_t p_code_section);
-    char* generate_code_case(char *str, const char *type_name, bool &else_branch);
+    char* generate_code_case(char *str, char*& def_glob_vars, char*& src_glob_vars,
+      const char *type_name, bool &else_branch);
 
     /** Needed by implicit omit. Pushes attrib path down to definitions
      */
@@ -1499,7 +1519,8 @@ namespace Ttcn {
      * construct */
     void chk_allowed_interleave();
     void set_code_section(GovernedSimple::code_section_t p_code_section);
-    char *generate_code(char *str, const char *type_name, const char* loc);
+    char *generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars,
+      const char *type_name, const char* loc);
 
     /** Needed by implicit omit. Pushes attrib path down to definitions
      */
@@ -1625,10 +1646,11 @@ namespace Ttcn {
     /** Generates the equivalent C++ code for the branches of an alt construct,
      * appends it to \a str and returns the resulting string. Parameter \a loc
      * shall contain the location of the alt construct. */
-    char *generate_code_alt(char *str, const Location& loc);
+    char *generate_code_alt(char *str, char*& def_glob_vars, char*& src_glob_vars,
+      const Location& loc);
     /** Generates the equivalent C++ code for the branches of an altstep,
      * appends it to \a str and returns the resulting string. */
-    char *generate_code_altstep(char *str);
+    char *generate_code_altstep(char *str, char*& def_glob_vars, char*& src_glob_vars);
     /** Generates the equivalent C++ code for the response and
      *  exception handling part of a call statement, appends it to \a
      *  str and returns the resulting string. Parameter \a loc
@@ -1638,8 +1660,8 @@ namespace Ttcn {
      *  those alt branches that contain receiving statement(s) are not
      *  generated but a "goto label_str_branch_n" where 'n' is the
      *  branch number. */
-    char* generate_code_call_body(char *str, const Location& loc,
-                                  const string& temp_id, bool in_interleave);
+    char* generate_code_call_body(char *str, char*& def_glob_vars, char*& src_glob_vars,
+      const Location& loc, const string& temp_id, bool in_interleave);
     void ilt_generate_code_call_body(ILT *ilt, const char *label_str);
   };
 
diff --git a/compiler2/ttcn3/TtcnTemplate.cc b/compiler2/ttcn3/TtcnTemplate.cc
index 69662da56..b07ec0203 100644
--- a/compiler2/ttcn3/TtcnTemplate.cc
+++ b/compiler2/ttcn3/TtcnTemplate.cc
@@ -3099,9 +3099,6 @@ end:
   {
     if (get_code_generated()) return str;
     set_code_generated();
-    if (err_descr) {
-      str = err_descr->generate_code_init_str(str, string(name)+"_err_descr");
-    }
     switch (templatetype) {
     case OMIT_VALUE:
     case ANY_VALUE:
@@ -3179,9 +3176,6 @@ end:
       str = length_restriction->generate_code_init(str, name);
     }
     if (is_ifpresent) str = mputprintf(str, "%s.set_ifpresent();\n", name);
-    if (err_descr) {
-      str = mputprintf(str, "%s.set_err_descr(&%s_err_descr);\n", name, name);
-    }
     return str;
   }
 
diff --git a/compiler2/ttcn3/compiler.l b/compiler2/ttcn3/compiler.l
index ca2f23b0c..a251832aa 100644
--- a/compiler2/ttcn3/compiler.l
+++ b/compiler2/ttcn3/compiler.l
@@ -531,7 +531,7 @@ xor4b		RETURN(Xor4bKeyword);
 "@try"    RETURN(TitanSpecificTryKeyword);
 "@catch"  RETURN(TitanSpecificCatchKeyword);
 "@profiler" RETURN(TitanSpecificProfilerKeyword);
-
+"@update" RETURN(TitanSpecificUpdateKeyword);
 
 	/* Predefined function identifiers */
 
diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y
index f37210107..fae8af54b 100644
--- a/compiler2/ttcn3/compiler.y
+++ b/compiler2/ttcn3/compiler.y
@@ -777,6 +777,7 @@ static const string anyname("anytype");
 %token TitanSpecificTryKeyword
 %token TitanSpecificCatchKeyword
 %token TitanSpecificProfilerKeyword
+%token TitanSpecificUpdateKeyword
 
 /* Keywords combined with a leading dot */
 
@@ -981,7 +982,7 @@ static const string anyname("anytype");
   StartTimerStatement StopExecutionStatement StopStatement StopTCStatement
   StopTimerStatement TimeoutStatement TimerStatements TriggerStatement
   UnmapStatement VerdictStatements WhileStatement SelectCaseConstruct
-  SelectUnionConstruct
+  SelectUnionConstruct UpdateStatement
   StopTestcaseStatement String2TtcnStatement ProfilerStatement int2enumStatement
 %type <statementblock> StatementBlock optElseClause FunctionStatementOrDefList
   ControlStatementOrDefList ModuleControlBody
@@ -1381,6 +1382,7 @@ UnionFieldDef
 UnionFieldDefList
 UnmapStatement
 UnnamedPart
+UpdateStatement
 UpperBound
 Value
 ValueList
@@ -4077,6 +4079,7 @@ FunctionStatement: // 180
 | StopTestcaseStatement { $$ = $1; }
 | ProfilerStatement { $$ = $1; }
 | int2enumStatement { $$ = $1; }
+| UpdateStatement { $$ = $1; }
 ;
 
 FunctionInstance: /* refpard */ // 181
@@ -5247,6 +5250,7 @@ ControlStatement: /* Statement *stmt */ // 295
 | StopExecutionStatement { $$ = $1; }
 | ProfilerStatement { $$ = $1; }
 | int2enumStatement { $$ = $1; }
+| UpdateStatement { $$ = $1; }
 ;
 
 /* A.1.6.2.1 Variable instantiation */
@@ -8131,6 +8135,22 @@ int2enumStatement:
   }
 ;
 
+UpdateStatement:
+  TitanSpecificUpdateKeyword '(' Reference ')' optWithStatement
+  {
+    Ttcn::Reference* ref;
+    if ($3.is_ref) {
+      ref = $3.ref;
+    }
+    else {
+      ref = new Ttcn::Reference($3.id);
+      ref->set_location(infile, @3);
+    }
+    $$ = new Statement(Statement::S_UPDATE, ref, $5);
+    $$->set_location(infile, @$);
+  }
+;
+
 ProfilerRunningOp:
   TitanSpecificProfilerKeyword DotRunningKeyword
   {
diff --git a/function_test/Semantic_Analyser/Makefile.semantic b/function_test/Semantic_Analyser/Makefile.semantic
index 62da9eb27..70d89e865 100644
--- a/function_test/Semantic_Analyser/Makefile.semantic
+++ b/function_test/Semantic_Analyser/Makefile.semantic
@@ -14,7 +14,7 @@
 ##############################################################################
 SADIRS := ver xer encode param template any_from pattern_ref float recof_index
 ifeq ($(RT2), yes)
-SADIRS += deprecated
+SADIRS += deprecated erroneous_attributes
 endif
 #$(wildcard TTCN3_[a0-9]* ASN_[a0-9]*) ver xer
 
diff --git a/function_test/Semantic_Analyser/erroneous_attributes/.gitignore b/function_test/Semantic_Analyser/erroneous_attributes/.gitignore
new file mode 100644
index 000000000..e2d293255
--- /dev/null
+++ b/function_test/Semantic_Analyser/erroneous_attributes/.gitignore
@@ -0,0 +1,2 @@
+!Makefile
+!*.ttcn
diff --git a/function_test/Semantic_Analyser/erroneous_attributes/ErroneousAttributes_SE.ttcn b/function_test/Semantic_Analyser/erroneous_attributes/ErroneousAttributes_SE.ttcn
new file mode 100644
index 000000000..37863d77f
--- /dev/null
+++ b/function_test/Semantic_Analyser/erroneous_attributes/ErroneousAttributes_SE.ttcn
@@ -0,0 +1,178 @@
+/******************************************************************************
+ * Copyright (c) 2000-2017 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Baranyi, Botond
+ *
+ ******************************************************************************/
+
+module ErroneousAttributes_SE { //^In TTCN-3 module//
+
+type record Rec {
+  integer num,
+  charstring str
+}
+
+type record of integer IntList;
+
+type union Uni {
+  integer i,
+  charstring cs,
+  IntList list
+}
+
+type charstring StringArray[3];
+
+type enumerated Enum { val1, val2 };
+
+function f_int(in integer x) return integer {
+  return x;
+}
+
+
+const Rec c_rec := { num := 1, str := "a" } with { erroneous(num) "value := 6" }
+
+const IntList c_list := { 1, 2, 3 } with { erroneous([1]) "after := omit all" }
+
+const Uni c_uni := { i := 10 } with { erroneous(i) "value := charstring:\"abc\"" }
+
+const StringArray c_arr := { "x", "y", "z" } with { erroneous([1]) "before := omit all" } //^In constant definition// //field qualifiers are only allowed for record, set and union types//
+
+const integer c_int := -5 with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute//
+
+const float c_float := 12.5 with { erroneous(not_a_field) "value := 100" } //^In constant definition// //field qualifiers are only allowed for record, set and union types//
+
+const boolean c_bool := true with { erroneous([0]) "value := 100" } //^In constant definition// //field qualifiers are only allowed for record, set and union types//
+
+const bitstring c_bit := '1010'B with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute//
+
+const hexstring c_hex := '1234ABC'H with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute//
+
+const octetstring c_oct := '1234ABCD'O with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute//
+
+const charstring c_char := "xyz" with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute//
+
+const universal charstring c_unichar := char(0, 0, 1, 117) with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute//
+
+const objid c_objid := objid { 0 1 10 } with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute//
+
+const Enum c_enum := val1 with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute//
+
+template Rec t_rec := { num := 1, str := "a" } with { erroneous(num) "after := omit all" }
+
+template IntList t_list := { 1, 2, 3 } with { erroneous([0]) "value := c_rec" }
+
+template Uni t_uni := { list := { 1, 2, 3 } } with { erroneous(list[1]) "value := f_int(5)" } //^In template definition// //Reference to a constant value was expected instead of the return value of function//
+
+template Rec t_rec_pard(in integer p) := { num := p, str := "a" } with { erroneous(str) "value := charstring: \"a\"" }
+
+template IntList t_list_pard(in integer p) := { 1, 2, p } with { erroneous([1]) "value := p" } //^In template definition// //There is no local or imported definition with name `p'//
+
+template Uni t_uni_pard(in charstring p) := { cs := p } with { erroneous(cs) "value := 10" }
+
+
+function f_bad_updates() { //^In function definition//
+  @update(c_int) with { erroneous "value := -1" } //^In @update statement// //Type `integer' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute//
+  @update(c_float) with { erroneous "value := -1" } //^In @update statement// //Type `float' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute//
+  @update(c_bool) with { erroneous "value := -1" } //^In @update statement// //Type `boolean' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute//
+  @update(c_bit) with { erroneous "value := -1" } //^In @update statement// //Type `bitstring' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute//
+  @update(c_hex) with { erroneous "value := -1" } //^In @update statement// //Type `hexstring' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute//
+  @update(c_oct) with { erroneous "value := -1" } //^In @update statement// //Type `octetstring' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute//
+  @update(c_char) with { erroneous "value := -1" } //^In @update statement// //Type `charstring' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute//
+  @update(c_unichar) with { erroneous "value := -1" } //^In @update statement// //Type `universal charstring' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute//
+  @update(c_objid) with { erroneous "value := -1" } //^In @update statement// //Type `objid' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute//
+  @update(c_enum) with { erroneous "value := -1" } //^In @update statement// //Type `@ErroneousAttributes_SE.Enum' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute//
+  @update(c_arr) with { erroneous "value := -1" } //^In @update statement// //Type `charstring\[3\]' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute//
+
+  @update(c_rec) with { encode "XML" } //^In @update statement// //Only `erroneous' attributes are allowed in an `@update' statement//
+  @update(c_rec) with { optional "implicit omit" } //^In @update statement// //Only `erroneous' attributes are allowed in an `@update' statement//
+  @update(c_rec) with { variant(num) "name as 'int'" } //^In @update statement// //Only `erroneous' attributes are allowed in an `@update' statement//
+  @update(c_rec) with { extension "transparent" } //^In @update statement// //Only `erroneous' attributes are allowed in an `@update' statement//
+  @update(c_rec) with { display(str) "red" } //^In @update statement// //Only `erroneous' attributes are allowed in an `@update' statement//
+  
+  @update(c_rec) with { erroneous "value := -1" } //^In @update statement// //At least one qualifier must be specified for the `erroneous' attribute//
+  @update(c_rec) with { erroneous(not_a_field) "value := -1" } //^In @update statement// //Reference to non-existent field `not_a_field' in type `@ErroneousAttributes_SE.Rec'//
+  @update(c_rec.num) with { erroneous "value := -1" } //^In @update statement// //At least one qualifier must be specified for the `erroneous' attribute// //Field names and array indexes are not allowed in this context//
+  
+  @update(f_good_updates); //^In @update statement// //Reference to constant or template definition was expected instead of function//
+  @update(CT); //^In @update statement// //Reference to constant or template definition was expected instead of type//
+  @update(Sig); //^In @update statement// //Reference to constant or template definition was expected instead of type//
+  @update(PT); //^In @update statement// //Reference to constant or template definition was expected instead of type//
+}
+
+signature Sig(inout integer p);
+
+type port PT procedure {
+  inout Sig;
+}
+with {
+  extension "internal";
+}
+
+type component CT {
+  port PT pt;
+}
+
+function f_good_updates() runs on CT {
+  var integer v := 0;
+  @update(c_rec) with { erroneous(str) "value := c_rec.num" }
+  
+  while (v > 0) {
+    @update(c_list) with { erroneous([1]) "before := omit all" }
+  }
+  
+  if (v == 10) {
+    @update(c_uni) with { erroneous(i) "value := f_int(3)" }
+  }
+  else {
+    @update(t_rec) with { erroneous(num) "after := omit all" }
+  }
+  
+  do {
+    @update(t_list) with { erroneous([2]) "value := f_int(v)" }
+  }
+  while (v < 10);
+  
+  for (v := 1; v < 2; v := v + 1) {
+    @update(t_uni) with { erroneous(list[0]) "value := f_int(v + 1) * (v - 10)" }
+  }
+  
+  select (v) {
+    case (1) {
+      @update(t_rec_pard) with { erroneous(str) "value := valueof(t_rec_pard(v))" }
+    }
+    case else {
+      @update(t_list_pard) with { erroneous([1]) "value := int2str(v)" }
+    }
+  }
+  
+  timer tmr;
+  tmr.start(0.1);
+  alt {
+    [] tmr.timeout {
+      @update(t_uni_pard) with { erroneous(cs) "value := encvalue(c_list)" }
+    }
+  }
+  
+  interleave {
+    [] tmr.timeout {
+      @update(c_rec) with { erroneous(str) "value := omit" }
+    }
+  }
+  
+  pt.call(Sig : { v }, 0.3) {
+    [] pt.catch(timeout) {
+      @update(c_rec) with { erroneous(str) "before := omit all" }
+    }
+  }
+}
+
+}
+with {
+  encode "JSON";
+  extension "anytype integer,Rec";
+}
diff --git a/function_test/Semantic_Analyser/erroneous_attributes/Makefile b/function_test/Semantic_Analyser/erroneous_attributes/Makefile
new file mode 100644
index 000000000..3f369041a
--- /dev/null
+++ b/function_test/Semantic_Analyser/erroneous_attributes/Makefile
@@ -0,0 +1,12 @@
+##############################################################################
+# Copyright (c) 2000-2017 Ericsson Telecom AB
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#   Baranyi, Botond
+#
+##############################################################################
+include ../common.mk
diff --git a/function_test/Semantic_Analyser/erroneous_attributes/t b/function_test/Semantic_Analyser/erroneous_attributes/t
new file mode 100755
index 000000000..3a4b58ec1
--- /dev/null
+++ b/function_test/Semantic_Analyser/erroneous_attributes/t
@@ -0,0 +1,9 @@
+#!/usr/bin/perl
+# note this is called through "perl -w"
+use strict;
+
+my $self = $0;
+$self =~ s!/t!!;
+
+exec('make check --no-print-directory -s -C ' . $self);
+
diff --git a/regression_test/negativeTest/Makefile b/regression_test/negativeTest/Makefile
index a53a77cc0..00307792a 100644
--- a/regression_test/negativeTest/Makefile
+++ b/regression_test/negativeTest/Makefile
@@ -42,7 +42,7 @@ TTCN3_MODULES = negtest.ttcn NegTestTestcases.ttcn \
 NegTest_TEXT_Types.ttcn NegTest_TEXT_Testcases.ttcn \
 NegTest_RAW_Types.ttcn NegTest_RAW_Testcases.ttcn \
 www_XmlTest_org_negativeTest_XML_Types.ttcn NegTest_XML_Testcases.ttcn XSD.ttcn UsefulTtcn3Types.ttcn \
-NegTest_JSON.ttcn
+NegTest_JSON.ttcn NegTest_Update.ttcn
 
 ASN1_MODULES = Types.asn NegTestTypes.asn
 
diff --git a/regression_test/negativeTest/NegTest_JSON.cfg b/regression_test/negativeTest/NegTest_JSON.cfg
index c0cc0059c..6da448dab 100644
--- a/regression_test/negativeTest/NegTest_JSON.cfg
+++ b/regression_test/negativeTest/NegTest_JSON.cfg
@@ -19,3 +19,4 @@ LogEventTypes := Detailed
 
 [EXECUTE]
 NegTest_JSON.control
+NegTest_Update.control
diff --git a/regression_test/negativeTest/NegTest_JSON.ttcn b/regression_test/negativeTest/NegTest_JSON.ttcn
index 33217c4ca..7511862f9 100644
--- a/regression_test/negativeTest/NegTest_JSON.ttcn
+++ b/regression_test/negativeTest/NegTest_JSON.ttcn
@@ -102,6 +102,15 @@ const Uni2 u7 := { i := -6 } with { erroneous(i) "value := omit" };
 const Uni2 u8 := { i := -6 } with { erroneous(i) "value(raw) := \"abc\" & char(0,0,1,117)" };
 const Uni2 u9 := { list := { 0, 3, 6 } } with { erroneous(list[2]) "value := \"6\"" };
 
+template Rec r_temp := { num := 10, str := "hello" } with { erroneous(str) "value := r1" }; // the erroneous attributes of r1 will also affect the result
+
+template Set s_temp_pard(IntList p1, Rec p2) := { list := p1, rec := p2, b := true } with { erroneous(list) "after := omit all" }; // the erroneous attributes of p1 will also affect the result
+
+/* Erroneous constants and templates imported by other modules */
+const IntList c_imp := { 1, 12, 23, 34 } with { erroneous([3]) "value := 234" };
+template Rec t_imp := { num := 10, str := "hello" };
+template Uni2 t_imp_pard(IntList p) := { list := p } with { erroneous(list[0]) "after := omit all" };
+
 /******** Test cases for record-ofs ********/
 testcase tc_record_of_omit_before() runs on CT {
   var octetstring res := f_enc_il(il1);
@@ -279,6 +288,19 @@ testcase tc_union_as_value_alternative_is_erroneous() runs on CT {
   else { setverdict(fail, res); }
 }
 
+/********* Test cases for templates *********/
+testcase tc_record_template_other_value() runs on CT {
+  var octetstring res := f_enc_r(valueof(r_temp));
+  if (char2oct("{\"num\":10,\"str\":{\"BOOLEAN\":true,\"num\":10,\"str\":\"hello\"}}") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_set_template_pard_omit_after() runs on CT {
+  var octetstring res := f_enc_s(valueof(s_temp_pard(il1, r1)));
+  if (char2oct("{\"list\":[2,3]}") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
 /*************** Control part ***************/
 control {
   execute(tc_record_of_omit_before());
@@ -310,6 +332,8 @@ control {
   execute(tc_union_as_value_omit_value());
   execute(tc_union_as_value_raw_value());
   execute(tc_union_as_value_alternative_is_erroneous());
+  execute(tc_record_template_other_value());
+  execute(tc_set_template_pard_omit_after());
 }
 
 }
diff --git a/regression_test/negativeTest/NegTest_Update.ttcn b/regression_test/negativeTest/NegTest_Update.ttcn
new file mode 100644
index 000000000..cacc1ab58
--- /dev/null
+++ b/regression_test/negativeTest/NegTest_Update.ttcn
@@ -0,0 +1,231 @@
+/******************************************************************************
+ * Copyright (c) 2000-2017 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Baranyi, Botond
+ *
+ ******************************************************************************/
+
+// This module tests the '@update' statement, which changes the erroneous
+// attributes of constants and templates.
+module NegTest_Update {
+
+import from NegTest_JSON all;
+
+/************* Erroneous constants **************/
+const IntList c1 := { 1, 3, 5, 7, 9 } with { erroneous([2]) "value := 99" }
+const Rec c2 := { num := 3, str := "abc" } with { erroneous(num) "value := float: 3.0" }
+const Uni c3 := { list := { 10, 20 } } // not erroneous initially
+
+/************* Erroneous templates **************/
+template IntListList t1 := { { 1, 2, 3 }, { 10, 20, 30 }, { 100, 200, 300 } } with { erroneous([1][0]) "after := omit all"; erroneous([2][2]) "before := omit all" };
+template Set t2 := { list := { 0, 3, 6 }, rec := { num := 100, str := "aeiou" }, b := true };
+template Uni2 t3 := { i := 10 } with { erroneous(i) "value := charstring: \"ten\"" };
+
+template IntList t_pard1(integer p) := { 0, p } with { erroneous([1]) "value := Rec: { num := 0, str := \"xy\" }" };
+template Rec t_pard2(template integer p) := { num := p, str := "p" } with { erroneous(num) "after := omit all" };
+template Uni t_pard3(template Uni p) := p;
+
+/******* Test cases for local constants *********/
+testcase tc_update_const_change() runs on CT {
+  @update(c1) with { erroneous([1]) "before := omit all" }
+  var octetstring res := f_enc_il(c1);
+  if (char2oct("[3,5,7,9]") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_const_remove() runs on CT {
+  @update(c2); // removes the erroneous attributes
+  var octetstring res := f_enc_r(c2);
+  if (char2oct("{\"num\":3,\"str\":\"abc\"}") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_const_add() runs on CT {
+  @update(c3) with { erroneous(list[1]) "value := Rec: { num := 0, str := \"xy\" }" }
+  var octetstring res := f_enc_u(c3);
+  if (char2oct("{\"list\":[10,{\"num\":0,\"str\":\"xy\"}]}") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_const_reset() runs on CT {
+  @update(c3);
+  var octetstring res := f_enc_u(c3);
+  if (char2oct("{\"list\":[10,20]}") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+/******* Test cases for local templates *********/
+testcase tc_update_temp_change() runs on CT {
+  @update(t1) with { erroneous([1]) "value := charstring: \"aaaa\""; erroneous([2]) "after := il4" /* from the imported module */ };
+  var octetstring res := f_enc_ill(valueof(t1));
+  if (char2oct("[[1,2,3],\"aaaa\",[100,200,300],[4,5,false,6]]") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_temp_add() runs on CT {
+  @update(t2) with { erroneous(rec.str) "value := omit" };
+  var octetstring res := f_enc_s(valueof(t2));
+  if (char2oct("{\"list\":[0,3,6],\"rec\":{\"num\":100},\"b\":true}") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_temp_remove() runs on CT {
+  @update(t3);
+  var octetstring res := f_enc_u2(valueof(t3));
+  if (char2oct("10") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_temp_readd() runs on CT {
+  @update(t3) with { erroneous(i) "value := float: 123.456" };
+  var octetstring res := f_enc_u2(valueof(t3));
+  if (char2oct("123.456000") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+/* Test cases for local parameterized templates */
+testcase tc_update_temp_pard_change() runs on CT {
+  @update(t_pard1) with { erroneous([0]) "after := IntList: { 1, 2, 4 }" };
+  var octetstring res := f_enc_il(valueof(t_pard1(7)));
+  if (char2oct("[0,[1,2,4],7]") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_temp_pard_remove() runs on CT {
+  @update(t_pard2);
+  var octetstring res := f_enc_r(valueof(t_pard2(-9)));
+  if (char2oct("{\"num\":-9,\"str\":\"p\"}") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_temp_pard_add() runs on CT {
+  @update(t_pard3) with { erroneous(i) "value := float: 0.123456" };
+  var octetstring res := f_enc_u(valueof(t_pard3(Uni: { i := 700 })));
+  if (char2oct("{\"i\":0.123456}") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_temp_pard_reset() runs on CT {
+  @update(t_pard3);
+  var octetstring res := f_enc_u(valueof(t_pard3(Uni: { i := 700 })));
+  if (char2oct("{\"i\":700}") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+/* Test cases for imported constants and templates */
+testcase tc_update_imported_const_change() runs on CT {
+  @update(c_imp) with { erroneous([1]) "before := omit all" };
+  var octetstring res := f_enc_il(c_imp);
+  if (char2oct("[12,23,34]") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_imported_temp_add() runs on CT {
+  @update(t_imp) with { erroneous(str) "value := \"bye\"" };
+  var octetstring res := f_enc_r(valueof(t_imp));
+  if (char2oct("{\"num\":10,\"str\":\"bye\"}") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_imported_temp_pard_remove() runs on CT {
+  @update(t_imp_pard);
+  var octetstring res := f_enc_u2(valueof(t_imp_pard(il2 /* from the imported module */)));
+  if (char2oct("[0,1,2]") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_imported_temp_pard_readd() runs on CT {
+  @update(t_imp_pard) with { erroneous(list[0]) "after := omit all" };
+  var octetstring res := f_enc_u2(valueof(t_imp_pard(il2)));
+  if (char2oct("[0]") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+/* Test case for using '@update' in an interleave statement */
+testcase tc_update_in_interleave() runs on CT {
+  var octetstring res1, res2;
+  timer tmr1, tmr2;
+  tmr1.start(0.5);
+  tmr2.start(0.5);
+  interleave {
+    [] tmr1.timeout {
+      @update(t1) with { erroneous([1][0]) "after := omit all"; erroneous([2][2]) "before := omit all" };
+      res1 := f_enc_ill(valueof(t1));
+    }
+    [] tmr2.timeout {
+      @update(c_imp) with { erroneous([3]) "value := 234" };
+    }
+  }
+  res2 := f_enc_il(c_imp);
+  if (char2oct("[[1,2,3],[10],[300]]") == res1 and
+      char2oct("[1,12,23,234]") == res2) {
+    setverdict(pass);
+  }
+  else {
+    setverdict(fail, res1, " ", res2);
+  }
+}
+
+/* Test cases with dynamic values in attribute specs */
+testcase tc_update_const_change_dynamic() runs on CT {
+  var integer v := 17;
+  @update(c1) with { erroneous([1]) "value := v"; erroneous([4]) "value := 2 * v - 5" }
+  var octetstring res := f_enc_il(c1);
+  if (char2oct("[1,17,5,7,29]") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+function f_return_list(in integer p) return IntList {
+  var IntList ret_val;
+  for (var integer i := 0; i < p; i := i + 1) {
+    ret_val[i] := i;
+  }
+  return ret_val;
+}
+
+testcase tc_update_temp_change_dynamic() runs on CT {
+  var Rec v := { num := 6, str := "something" };
+  @update(t2) with { erroneous(list) "value := v"; erroneous(rec) "value := f_return_list(6)" }
+  var octetstring res := f_enc_s(valueof(t2));
+  if (char2oct("{\"list\":{\"num\":6,\"str\":\"something\"},\"rec\":[0,1,2,3,4,5],\"b\":true}") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+testcase tc_update_imported_temp_pard_change_dynamic() runs on CT {
+  var IntList v := { 2, 4, 8, 16 };
+  @update(t_imp_pard) with { erroneous(list) "value := f_return_list(v[1]) & { v[2] } & v" }
+  var octetstring res := f_enc_u2(valueof(t_imp_pard(v)));
+  if (char2oct("[0,1,2,3,8,2,4,8,16]") == res) { setverdict(pass); }
+  else { setverdict(fail, res); }
+}
+
+/*************** Control part ***************/
+control {
+  execute(tc_update_const_change());
+  execute(tc_update_const_remove());
+  execute(tc_update_const_add());
+  execute(tc_update_const_reset());
+  execute(tc_update_temp_change());
+  execute(tc_update_temp_add());
+  execute(tc_update_temp_remove());
+  execute(tc_update_temp_readd());
+  execute(tc_update_temp_pard_change());
+  execute(tc_update_temp_pard_remove());
+  execute(tc_update_temp_pard_add());
+  execute(tc_update_temp_pard_reset());
+  execute(tc_update_imported_const_change());
+  execute(tc_update_imported_temp_add());
+  execute(tc_update_imported_temp_pard_remove());
+  execute(tc_update_imported_temp_pard_readd());
+  execute(tc_update_in_interleave());
+  execute(tc_update_const_change_dynamic());
+  execute(tc_update_temp_change_dynamic());
+  execute(tc_update_imported_temp_pard_change_dynamic());
+}
+
+}
diff --git a/regression_test/negativeTest/NegTest_all.cfg b/regression_test/negativeTest/NegTest_all.cfg
index f37427d77..7fa14c846 100644
--- a/regression_test/negativeTest/NegTest_all.cfg
+++ b/regression_test/negativeTest/NegTest_all.cfg
@@ -25,5 +25,6 @@ NegTest_TEXT_Testcases.control
 NegTest_XML_Testcases.control
 NegTest_RAW_Testcases.control
 NegTest_JSON.control
+NegTest_Update.control
 
 //saved by GUI
-- 
GitLab