[Python-checkins] python/dist/src/Python symtable.c, 2.10.8.23, 2.10.8.24 newcompile.c, 1.1.2.87, 1.1.2.88

jhylton at users.sourceforge.net jhylton at users.sourceforge.net
Thu Apr 8 14:00:50 EDT 2004


Update of /cvsroot/python/python/dist/src/Python
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4670/Python

Modified Files:
      Tag: ast-branch
	symtable.c newcompile.c 
Log Message:
Fix several symbol table/compiler problems uncovered by test_scope.

The symbol table wasn't accounting for nested class blocks with local
variables that shadow free variables of methods.  In this case, the
name is added to co_freevars and is treated as a local variable.  The
generated code calls LOAD_CLOSURE to pass the cell for the free
variable from the enclosing scope through to the methods.

XXX Use the DEF_FREE_CLASS flag to mark classes where the same name is
local and free.  This is a bit of a hack, because it's a scoping issue
and scope decisions are stored elsewhere.  But the bits used for
scoping decisions are densely packed (1, 2, 3, 4, and 5 all have
meanings) instead of being a bitfield.  Perhaps we should revise that
and use one bit for each scope; then this special case would just have
two scope bits set.

Modify dictbytype() to take a scope and a flag, so that free variables
can be extracted correctly.

Cell variables were being created in class blocks, which doesn't make
any sense.  A class can never have a cell variable, because it's
bindings aren't visible to nested scopes.

Global variables weren't being handled properly.  Extend
analyze_block() to thread through a set containing the current defined
global variables.  A bizarre feature of the scoping rules is that a
global statement in an enclosing scope affects a nested scope.


Index: symtable.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/symtable.c,v
retrieving revision 2.10.8.23
retrieving revision 2.10.8.24
diff -C2 -d -r2.10.8.23 -r2.10.8.24
*** symtable.c	21 Mar 2004 20:49:13 -0000	2.10.8.23
--- symtable.c	8 Apr 2004 18:00:46 -0000	2.10.8.24
***************
*** 318,332 ****
  }
  
! /* XXX handle this:
!    def f(x):
!        global y
!        def g():
!            return x + y
!    so that y is a global
  */
  
  static int 
  analyze_name(PyObject *dict, PyObject *name, int flags, PyObject *bound,
! 	     PyObject *local, PyObject *free, int nested)
  {
  	if (flags & DEF_GLOBAL) {
--- 318,329 ----
  }
  
! /* Decide on scope of name, given flags.
! 
!    The dicts passed in as arguments are modified as necessary.
  */
  
  static int 
  analyze_name(PyObject *dict, PyObject *name, int flags, PyObject *bound,
! 	     PyObject *local, PyObject *free, PyObject *global, int nested)
  {
  	if (flags & DEF_GLOBAL) {
***************
*** 334,337 ****
--- 331,340 ----
  			return 0; /* can't declare a parameter as global */
  		SET_SCOPE(dict, name, GLOBAL_EXPLICIT);
+ 		if (PyDict_SetItem(global, name, Py_None) < 0)
+ 			return 0;
+ 		if (bound && PyDict_GetItem(bound, name)) {
+ 			if (PyDict_DelItem(bound, name) < 0)
+ 				return 0;
+ 		}
  		return 1;
  	}
***************
*** 340,347 ****
  		if (PyDict_SetItem(local, name, Py_None) < 0)
  			return 0;
  		return 1;
  	}
! 	/* If the function is nested, then it can have free vars. */
! 	if (nested && bound && PyDict_GetItem(bound, name)) {
  		SET_SCOPE(dict, name, FREE);
  		if (PyDict_SetItem(free, name, Py_None) < 0)
--- 343,358 ----
  		if (PyDict_SetItem(local, name, Py_None) < 0)
  			return 0;
+ 		if (PyDict_GetItem(global, name)) {
+ 			if (PyDict_DelItem(global, name) < 0)
+ 				return 0;
+ 		}
  		return 1;
  	}
! 	/* If an enclosing block has a binding for this name, it
! 	   is a free variable rather than a global variable.
! 	   Note that having a non-NULL bound implies that the block
! 	   is nested.
! 	*/
! 	if (bound && PyDict_GetItem(bound, name)) {
  		SET_SCOPE(dict, name, FREE);
  		if (PyDict_SetItem(free, name, Py_None) < 0)
***************
*** 349,352 ****
--- 360,370 ----
  		return 1;
  	}
+ 	/* If a parent has a global statement, then call it global
+ 	   explicit?  It could also be global implicit.
+ 	 */
+ 	else if (global && PyDict_GetItem(global, name)) {
+ 		SET_SCOPE(dict, name, GLOBAL_EXPLICIT);
+ 		return 1;
+ 	}
  	else {
  		SET_SCOPE(dict, name, GLOBAL_IMPLICIT);
***************
*** 400,404 ****
  static int
  update_symbols(PyObject *symbols, PyObject *scope, 
!                PyObject *bound, PyObject *free)
  {
  	PyObject *name, *v, *u, *w, *free_value = NULL;
--- 418,422 ----
  static int
  update_symbols(PyObject *symbols, PyObject *scope, 
!                PyObject *bound, PyObject *free, int class)
  {
  	PyObject *name, *v, *u, *w, *free_value = NULL;
***************
*** 422,439 ****
          free_value = PyInt_FromLong(FREE << SCOPE_OFF);
          if (!free_value)
!             return 0;
  
          /* add a free variable when it's only use is for creating a closure */
          pos = 0;
  	while (PyDict_Next(free, &pos, &name, &v)) {
!             if (PyDict_GetItem(symbols, name))
!                 continue;       /* it's not free, probably a cell */
!             if (!PyDict_GetItem(bound, name))
!                 continue;       /* it's a global */
  
!             if (PyDict_SetItem(symbols, name, free_value) < 0) {
!                 Py_DECREF(free_value);
!                 return 0;
!             }
          }
          Py_DECREF(free_value);
--- 440,479 ----
          free_value = PyInt_FromLong(FREE << SCOPE_OFF);
          if (!free_value)
! 		return 0;
  
          /* add a free variable when it's only use is for creating a closure */
          pos = 0;
  	while (PyDict_Next(free, &pos, &name, &v)) {
! 		PyObject *o = PyDict_GetItem(symbols, name);
  
! 		if (o) {
! 			/* It could be a free variable in a method of
! 			   the class that has the same name as a local
! 			   or global in the class scope.
! 			*/
! 			if  (class && 
! 			     PyInt_AS_LONG(o) & (DEF_BOUND | DEF_GLOBAL)) {
! 				int i = PyInt_AS_LONG(o) | DEF_FREE_CLASS;
! 				o = PyInt_FromLong(i);
! 				if (!o) {
! 					Py_DECREF(free_value);
! 					return 0;
! 				}
! 				if (PyDict_SetItem(symbols, name, o) < 0) {
! 					Py_DECREF(o);
! 					Py_DECREF(free_value);
! 					return 0;
! 				}
! 			}
! 			/* else it's not free, probably a cell */
! 			continue;
! 		}
! 		if (!PyDict_GetItem(bound, name))
! 			continue;       /* it's a global */
! 
! 		if (PyDict_SetItem(symbols, name, free_value) < 0) {
! 			Py_DECREF(free_value);
! 			return 0;
! 		}
          }
          Py_DECREF(free_value);
***************
*** 441,448 ****
  }   
  
  static int
! analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free)
  {
  	PyObject *name, *v, *local = NULL, *scope = NULL, *newbound = NULL;
  	int i, flags, pos = 0, success = 0;
  
--- 481,498 ----
  }   
  
+ /* Make final symbol table decisions for block of ste.
+    Arguments:
+    bound -- set of variables bound in enclosing scopes (input)
+    free -- set of free variables in enclosed scopes (output)
+    globals -- set of declared global variables in enclosing scopes (input)
+ */
+    
+ 
  static int
! analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, 
! 	      PyObject *global)
  {
  	PyObject *name, *v, *local = NULL, *scope = NULL, *newbound = NULL;
+ 	PyObject *newglobal = NULL;
  	int i, flags, pos = 0, success = 0;
  
***************
*** 453,456 ****
--- 503,524 ----
  	if (!scope)
  		goto error;
+ 	newglobal = PyDict_New();
+ 	if (!newglobal)
+ 		goto error;
+ 	newbound = PyDict_New();
+ 	if (!newbound)
+ 		goto error;
+ 
+ 	if (ste->ste_type == ClassBlock) {
+ 		/* make a copy of globals before calling analyze_name(),
+ 		   because global statements in the class have no effect
+ 		   on nested functions.
+ 		*/
+ 		if (PyDict_Update(newglobal, global) < 0)
+ 			goto error;
+ 		if (bound)
+ 			if (PyDict_Update(newbound, bound) < 0)
+ 				goto error;
+ 	}
  
  	assert(PySTEntry_Check(ste));
***************
*** 459,476 ****
  		flags = PyInt_AS_LONG(v);
  		if (!analyze_name(scope, name, flags, bound, local, free,
! 				  ste->ste_nested))
  			goto error;
  	}
  
! 	/* create a new bound dictionary to pass to children */
! 	newbound = PyDict_New();
! 	if (!newbound)
! 		goto error;
! 	if (ste->ste_type == FunctionBlock) {
! 		if (PyDict_Update(newbound, local) < 0)
! 			goto error;
! 	}
! 	if (bound) {
! 		if (PyDict_Update(newbound, bound) < 0)
  			goto error;
  	}
--- 527,544 ----
  		flags = PyInt_AS_LONG(v);
  		if (!analyze_name(scope, name, flags, bound, local, free,
! 				  global, ste->ste_nested))
  			goto error;
  	}
  
! 	if (ste->ste_type != ClassBlock) {
! 		if (ste->ste_type == FunctionBlock) {
! 			if (PyDict_Update(newbound, local) < 0)
! 				goto error;
! 		}
! 		if (bound) {
! 			if (PyDict_Update(newbound, bound) < 0)
! 				goto error;
! 		}
! 		if (PyDict_Update(newglobal, global) < 0)
  			goto error;
  	}
***************
*** 479,489 ****
  		PyObject *c = PyList_GET_ITEM(ste->ste_children, i);
  		assert(c && PySTEntry_Check(c));
! 		if (!analyze_block((PySTEntryObject *)c, newbound, free))
  			goto error;
  	}
  
! 	if (!analyze_cells(scope, free))
  		goto error;
! 	if (!update_symbols(ste->ste_symbols, scope, bound, free))
  		goto error;
  	success = 1;
--- 547,559 ----
  		PyObject *c = PyList_GET_ITEM(ste->ste_children, i);
  		assert(c && PySTEntry_Check(c));
! 		if (!analyze_block((PySTEntryObject *)c, newbound, free,
! 				   newglobal))
  			goto error;
  	}
  
! 	if (ste->ste_type == FunctionBlock && !analyze_cells(scope, free))
  		goto error;
! 	if (!update_symbols(ste->ste_symbols, scope, bound, free,
! 			    ste->ste_type == ClassBlock))
  		goto error;
  	success = 1;
***************
*** 492,495 ****
--- 562,566 ----
  	Py_XDECREF(scope);
  	Py_XDECREF(newbound);
+ 	Py_XDECREF(newglobal);
  	if (!success)
  		assert(PyErr_Occurred());
***************
*** 500,511 ****
  symtable_analyze(struct symtable *st)
  {
! 	PyObject *free;
  	int r;
  
  	free = PyDict_New();
  	if (!free)
! 		return 0;
! 	r = analyze_block(st->st_top, NULL, free);
  	Py_DECREF(free);
  	return r;
  }
--- 571,588 ----
  symtable_analyze(struct symtable *st)
  {
! 	PyObject *free, *global;
  	int r;
  
  	free = PyDict_New();
  	if (!free)
! 	    return 0;
! 	global = PyDict_New();
! 	if (!global) {
! 	    Py_DECREF(global);
! 	    return 0;
! 	}
! 	r = analyze_block(st->st_top, NULL, free, global);
  	Py_DECREF(free);
+ 	Py_DECREF(global);
  	return r;
  }

Index: newcompile.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/Attic/newcompile.c,v
retrieving revision 1.1.2.87
retrieving revision 1.1.2.88
diff -C2 -d -r1.1.2.87 -r1.1.2.88
*** newcompile.c	26 Mar 2004 18:17:31 -0000	1.1.2.87
--- newcompile.c	8 Apr 2004 18:00:46 -0000	1.1.2.88
***************
*** 338,343 ****
  }
  
  static PyObject *
! dictbytype(PyObject *src, int scope_type, int offset)
  {
  	int pos = 0, i = offset, scope;
--- 338,351 ----
  }
  
+ /* Return new dict containing names from src that match scope(s).
+ 
+    src is a symbol table dictionary.  If the scope of a name matches
+    either scope_type or flag is set, insert it into the new dict.  The
+    values are integers, starting at offset and increasing by one for
+    each key.
+ */
+ 
  static PyObject *
! dictbytype(PyObject *src, int scope_type, int flag, int offset)
  {
  	int pos = 0, i = offset, scope;
***************
*** 353,357 ****
              scope = (PyInt_AS_LONG(v) >> SCOPE_OFF) & SCOPE_MASK;
  
!             if (scope == scope_type) {
                  PyObject *tuple, *item = PyInt_FromLong(i);
                  if (item == NULL) {
--- 361,365 ----
              scope = (PyInt_AS_LONG(v) >> SCOPE_OFF) & SCOPE_MASK;
  
!             if (scope == scope_type || PyInt_AS_LONG(v) & flag) {
                  PyObject *tuple, *item = PyInt_FromLong(i);
                  if (item == NULL) {
***************
*** 423,428 ****
  	u->u_name = name;
  	u->u_varnames = list2dict(u->u_ste->ste_varnames);
! 	u->u_cellvars = dictbytype(u->u_ste->ste_symbols, CELL, 0);
! 	u->u_freevars = dictbytype(u->u_ste->ste_symbols, FREE, 
                                     PyDict_Size(u->u_cellvars));
  
--- 431,436 ----
  	u->u_name = name;
  	u->u_varnames = list2dict(u->u_ste->ste_varnames);
! 	u->u_cellvars = dictbytype(u->u_ste->ste_symbols, CELL, 0, 0);
! 	u->u_freevars = dictbytype(u->u_ste->ste_symbols, FREE, DEF_FREE_CLASS,
                                     PyDict_Size(u->u_cellvars));
  
***************
*** 1114,1120 ****
                            PyString_AS_STRING(name), 
                            PyString_AS_STRING(c->u->u_name), 
!                           PyObject_REPR(c->c_st->st_cur->ste_id),
                            c->c_filename,
!                           PyObject_REPR(c->c_st->st_cur->ste_symbols),
                            PyObject_REPR(c->u->u_varnames),
                            PyObject_REPR(c->u->u_names)
--- 1122,1128 ----
                            PyString_AS_STRING(name), 
                            PyString_AS_STRING(c->u->u_name), 
!                           PyObject_REPR(c->u->u_ste->ste_id),
                            c->c_filename,
!                           PyObject_REPR(c->u->u_ste->ste_symbols),
                            PyObject_REPR(c->u->u_varnames),
                            PyObject_REPR(c->u->u_names)
***************
*** 2029,2032 ****
--- 2037,2046 ----
  			break;
  		case Del:
+ 			PyErr_Format(PyExc_SyntaxError,
+ 				     "can not delete variable '%s' referenced "
+ 				     "in nested scope",
+ 				     PyString_AS_STRING(name));
+ 			return 0;
+ 			break;
  		case Param:
  			assert(0); /* impossible */
***************
*** 2042,2046 ****
  			break;
  		case Param:
!                     assert(0); /* impossible */
  		}
  		ADDOP_O(c, op, name, varnames);
--- 2056,2060 ----
  			break;
  		case Param:
! 			assert(0); /* impossible */
  		}
  		ADDOP_O(c, op, name, varnames);
***************
*** 2677,2681 ****
  	b->b_seen = 1;
  	b->b_startdepth = depth;
! 	fprintf("block %d\n", block);
  	for (i = 0; i < b->b_iused; i++) {
  		instr = &b->b_instr[i];
--- 2691,2695 ----
  	b->b_seen = 1;
  	b->b_startdepth = depth;
! 	fprintf(stderr, "block %d\n", block);
  	for (i = 0; i < b->b_iused; i++) {
  		instr = &b->b_instr[i];
***************
*** 2684,2688 ****
  		if (depth > maxdepth)
  			maxdepth = depth;
! 		fprintf("  %s %d\n", opnames[instr->i_opcode], depth);
  		if (instr->i_jrel || instr->i_jabs) {
  			maxdepth = stackdepth_walk(c, instr->i_target,
--- 2698,2702 ----
  		if (depth > maxdepth)
  			maxdepth = depth;
! 		fprintf(stderr, "  %s %d\n", opnames[instr->i_opcode], depth);
  		if (instr->i_jrel || instr->i_jabs) {
  			maxdepth = stackdepth_walk(c, instr->i_target,




More information about the Python-checkins mailing list