# HG changeset patch
# User Max Brister <max@2bass.com>
# Date 1334719460 21600
# Node ID da85a187da6dfec8ab8c1c41b45cfeae479e2640
# Parent  3a9a56999ce5138e4fb0732021a5b1ffe18735e4
Disallow new variables in nested functions (bug #36271)

* src/ov-usr-fcn.cc (octave_user_function::bind_automatic_vars):
Use force_varref.
* src/variables.cc (bind_ans): Use force_varref.

* src/pt-id.cc (tree_identifier::rvalue, tree_identifier::lvalue):
Identify static workspace errors.
* src/pt-id.h (tree_identifier::static_workspace_error): New function.

* src/symtab.cc (symbol_table::do_update_nest): Mark static workspaces.
* src/symtab.h (symbol_table::symbol_record::symbol_record_rep::is_added_static,
symbol_table::symbol_record::symbol_record_rep::mark_added_static,
symbol_table::symbol_record::symbol_record_rep::unmark_added_static,
symbol_table::symbol_record::is_added_static,
symbol_table::symbol_record::mark_added_static,
symbol_table::symbol_record::unmark_added_static, symbol_table::force_varref):
New functions.
(symbol_table::symbol_table): Initialize static_workspace to false.
(symbol_table::do_insert): Added force_add parameter. Mark records as
added_static.
(symbol_table::do_varref): Added force_add parameter.

* test/nest/test_nest.m: Added nest_eval tests.
* test/nest/module.mk: Added nest/nest_eval.m.

* test/nest/nest_eval.m: New file.

diff --git a/src/ov-usr-fcn.cc b/src/ov-usr-fcn.cc
--- a/src/ov-usr-fcn.cc
+++ b/src/ov-usr-fcn.cc
@@ -581,8 +581,8 @@
       // which might be redefined in a function.  Keep the old argn name
       // for backward compatibility of functions that use it directly.
 
-      symbol_table::varref ("argn") = arg_names;
-      symbol_table::varref (".argn.") = Cell (arg_names);
+      symbol_table::force_varref ("argn") = arg_names;
+      symbol_table::force_varref (".argn.") = Cell (arg_names);
 
       symbol_table::mark_hidden (".argn.");
 
@@ -590,8 +590,8 @@
       symbol_table::mark_automatic (".argn.");
     }
 
-  symbol_table::varref (".nargin.") = nargin;
-  symbol_table::varref (".nargout.") = nargout;
+  symbol_table::force_varref (".nargin.") = nargin;
+  symbol_table::force_varref (".nargout.") = nargout;
 
   symbol_table::mark_hidden (".nargin.");
   symbol_table::mark_hidden (".nargout.");
diff --git a/src/pt-id.cc b/src/pt-id.cc
--- a/src/pt-id.cc
+++ b/src/pt-id.cc
@@ -63,7 +63,8 @@
   if (error_state)
     return retval;
 
-  octave_value val = xsym ().find ();
+  symbol_table::symbol_record& my_sym = xsym ();
+  octave_value val = my_sym.find ();
 
   if (val.is_defined ())
     {
@@ -92,6 +93,8 @@
           retval = val;
         }
     }
+  else if (my_sym.is_added_static ())
+    static_workspace_error ();
   else
     eval_undefined_error ();
 
@@ -114,7 +117,12 @@
 octave_lvalue
 tree_identifier::lvalue (void)
 {
-  return octave_lvalue (&(xsym().varref ()));
+  symbol_table::symbol_record& my_sym = xsym ();
+
+  if (my_sym.is_added_static ())
+    static_workspace_error ();
+
+  return octave_lvalue (&(my_sym.varref ()));
 }
 
 tree_identifier *
diff --git a/src/pt-id.h b/src/pt-id.h
--- a/src/pt-id.h
+++ b/src/pt-id.h
@@ -109,6 +109,12 @@
 
   void eval_undefined_error (void);
 
+  void static_workspace_error (void)
+  {
+    ::error ("can not add variable \"%s\" to a static workspace",
+             name ().c_str ());
+  }
+
   tree_identifier *dup (symbol_table::scope_id scope,
                         symbol_table::context_id context) const;
 
diff --git a/src/symtab.cc b/src/symtab.cc
--- a/src/symtab.cc
+++ b/src/symtab.cc
@@ -1492,8 +1492,11 @@
         }
     }
   else if (nest_children.size())
-    for (table_iterator ti = table.begin (); ti != table.end (); ++ti)
-      ti->second.set_curr_fcn (curr_fcn);
+    {
+      static_workspace = true;
+      for (table_iterator ti = table.begin (); ti != table.end (); ++ti)
+        ti->second.set_curr_fcn (curr_fcn);
+    }
 
   for (std::vector<symbol_table*>::iterator iter = nest_children.begin ();
        iter != nest_children.end (); ++iter)
diff --git a/src/symtab.h b/src/symtab.h
--- a/src/symtab.h
+++ b/src/symtab.h
@@ -199,6 +199,10 @@
     // temporary variables forced into symbol table for parsing
     static const unsigned int forced = 128;
 
+    // this symbol may NOT become a variable.
+    // (symbol added to a static workspace)
+    static const unsigned int added_static = 256;
+
   private:
 
     class
@@ -348,6 +352,7 @@
       bool is_global (void) const { return storage_class & global; }
       bool is_persistent (void) const { return storage_class & persistent; }
       bool is_forced (void) const { return storage_class & forced; }
+      bool is_added_static (void) const {return storage_class & added_static; }
 
       void mark_local (void) { storage_class |= local; }
       void mark_automatic (void) { storage_class |= automatic; }
@@ -369,6 +374,7 @@
           storage_class |= persistent;
       }
       void mark_forced (void) { storage_class |= forced; }
+      void mark_added_static (void) { storage_class |= added_static; }
 
       void unmark_local (void) { storage_class &= ~local; }
       void unmark_automatic (void) { storage_class &= ~automatic; }
@@ -378,6 +384,7 @@
       void unmark_global (void) { storage_class &= ~global; }
       void unmark_persistent (void) { storage_class &= ~persistent; }
       void unmark_forced (void) { storage_class &= ~forced; }
+      void unmark_added_static (void) { storage_class &= ~added_static; }
 
       void init_persistent (void)
       {
@@ -535,6 +542,7 @@
     bool is_inherited (void) const { return rep->is_inherited (); }
     bool is_persistent (void) const { return rep->is_persistent (); }
     bool is_forced (void) const { return rep->is_forced (); }
+    bool is_added_static (void) const { return rep->is_added_static (); }
 
     void mark_local (void) { rep->mark_local (); }
     void mark_automatic (void) { rep->mark_automatic (); }
@@ -544,6 +552,7 @@
     void mark_global (void) { rep->mark_global (); }
     void mark_persistent (void) { rep->mark_persistent (); }
     void mark_forced (void) { rep->mark_forced (); }
+    void mark_added_static (void) { rep->mark_added_static (); }
 
     void unmark_local (void) { rep->unmark_local (); }
     void unmark_automatic (void) { rep->unmark_automatic (); }
@@ -553,6 +562,7 @@
     void unmark_global (void) { rep->unmark_global (); }
     void unmark_persistent (void) { rep->unmark_persistent (); }
     void unmark_forced (void) { rep->unmark_forced (); }
+    void unmark_added_static (void) { rep->unmark_added_static (); }
 
     void init_persistent (void) { rep->init_persistent (); }
 
@@ -1151,13 +1161,23 @@
 
   static octave_value& varref (const std::string& name,
                                scope_id scope = xcurrent_scope,
-                               context_id context = xdefault_context)
+                               context_id context = xdefault_context,
+                               bool force_add = false)
   {
     static octave_value foobar;
 
     symbol_table *inst = get_instance (scope);
 
-    return inst ? inst->do_varref (name, context) : foobar;
+    return inst ? inst->do_varref (name, context, force_add) : foobar;
+  }
+
+  // Convenience function to greatly simplify
+  // octave_user_function::bind_automatic_vars
+  static octave_value& force_varref (const std::string& name,
+                                     scope_id scope = xcurrent_scope,
+                                     context_id context = xdefault_context)
+  {
+    return varref (name, scope, context, true);
   }
 
   static octave_value varval (const std::string& name,
@@ -2054,6 +2074,9 @@
   // The associated user code (may be null).
   octave_user_function *curr_fcn;
 
+  // If true then no variables can be added.
+  bool static_workspace;
+
   // Map from names of global variables to values.
   static std::map<std::string, octave_value> global_table;
 
@@ -2094,7 +2117,7 @@
 
   symbol_table (scope_id scope)
     : my_scope (scope), table_name (), table (), nest_children (), nest_parent (0),
-    curr_fcn (0), persistent_table () { }
+    curr_fcn (0), static_workspace (false), persistent_table () { }
 
   ~symbol_table (void) { }
 
@@ -2222,18 +2245,23 @@
 
   octave_value do_builtin_find (const std::string& name);
 
-  symbol_record& do_insert (const std::string& name)
+  symbol_record& do_insert (const std::string& name, bool force_add = false)
   {
     table_iterator p = table.find (name);
 
     if (p == table.end ())
       {
-        symbol_record parent_symbol;
-
-        if (nest_parent && nest_parent->look_nonlocal (name, parent_symbol))
-          return table[name] = parent_symbol;
+        symbol_record ret (my_scope, name);
+
+        if (nest_parent && nest_parent->look_nonlocal (name, ret))
+          return table[name] = ret;
         else
-          return table[name] = symbol_record (my_scope, name, octave_value ());
+          {
+            if (static_workspace && ! force_add)
+              ret.mark_added_static ();
+
+            return table[name] = ret;
+          }
       }
     else
       return p->second;
@@ -2253,13 +2281,13 @@
       p->second.force_variable (context);
   }
 
-  octave_value& do_varref (const std::string& name, context_id context)
+  octave_value& do_varref (const std::string& name, context_id context, bool force_add)
   {
     table_iterator p = table.find (name);
 
     if (p == table.end ())
       {
-        symbol_record& sr = do_insert (name);
+        symbol_record& sr = do_insert (name, force_add);
 
         return sr.varref (context);
       }
diff --git a/src/variables.cc b/src/variables.cc
--- a/src/variables.cc
+++ b/src/variables.cc
@@ -1880,7 +1880,7 @@
         }
       else
         {
-          symbol_table::varref (ans) = val;
+          symbol_table::force_varref (ans) = val;
 
           if (print)
             val.print_with_name (octave_stdout, ans);
diff --git a/test/nest/module.mk b/test/nest/module.mk
--- a/test/nest/module.mk
+++ b/test/nest/module.mk
@@ -1,6 +1,7 @@
 nest_FCN_FILES = \
   nest/arg_nest.m \
   nest/arg_ret.m \
+  nest/nest_eval.m \
   nest/no_closure.m \
   nest/persistent_nest.m \
   nest/recursive_nest.m \
diff --git a/test/nest/test_nest.m b/test/nest/test_nest.m
--- a/test/nest/test_nest.m
+++ b/test/nest/test_nest.m
@@ -47,7 +47,12 @@
 %!test
 %! scope3;
 
+%!assert (nest_eval ("x = 5;", "x = 6;"), 6);
+%!assert (nest_eval ("x = 5;", "y = 6;"), 5);
+%!assert (nest_eval ("x = -5; x = abs (x);", "y = 6;"), 5);
+
 %!error <D' undefined near line 7> scope2
 %!error <handles to nested functions are not yet supported> no_closure (0)
 %!error <handles to nested functions are not yet supported> no_closure (1)
-
+%!error <can not add variable "y" to a static workspace> nest_eval ("y = 5;", "")
+%!error <can not add variable "y" to a static workspace> nest_eval ("y;", "")
