[Python-checkins] CVS: python/dist/src/Python compile.c,2.179,2.180

Jeremy Hylton jhylton@users.sourceforge.net
Wed, 28 Feb 2001 14:54:53 -0800


Update of /cvsroot/python/python/dist/src/Python
In directory usw-pr-cvs1:/tmp/cvs-serv6749/Python

Modified Files:
	compile.c 
Log Message:
Add warning/error handlin for problematic nested scopes cases as
described in PEP 227.

symtable_check_unoptimized() warns about import * and exec with "in"
when it is used in a function that contains a nested function with
free variables.  Warnings are issued unless nested scopes are in
effect, in which case these are SyntaxErrors.

symtable_check_shadow() warns about assignments in a function scope
that shadow free variables defined in a nested scope.  This will
always generate a warning -- and will behave differently with nested
scopes than without.

Restore full checking for free vars in children, even when nested
scopes are not enabled.  This is needed to support warnings for
shadowing. 

Change symtable_warn() to return an int-- the return value of
PyErr_WarnExplicit. 

Sundry cleanup: Remove commented out code.  Break long lines.




Index: compile.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/compile.c,v
retrieving revision 2.179
retrieving revision 2.180
diff -C2 -r2.179 -r2.180
*** compile.c	2001/02/28 22:08:12	2.179
--- compile.c	2001/02/28 22:54:51	2.180
***************
*** 313,317 ****
  		Py_INCREF(lnotab);
  		co->co_lnotab = lnotab;
- /*		PyObject_Print((PyObject *)co, stderr, 0); */
  	}
  	return co;
--- 313,316 ----
***************
*** 1152,1156 ****
  			break;
  		case 'x':
! 			if (isxdigit(Py_CHARMASK(s[0])) && isxdigit(Py_CHARMASK(s[1]))) {
  				unsigned int x = 0;
  				c = Py_CHARMASK(*s);
--- 1151,1156 ----
  			break;
  		case 'x':
! 			if (isxdigit(Py_CHARMASK(s[0])) 
! 			    && isxdigit(Py_CHARMASK(s[1]))) {
  				unsigned int x = 0;
  				c = Py_CHARMASK(*s);
***************
*** 1174,1178 ****
  				break;
  			}
! 			PyErr_SetString(PyExc_ValueError, "invalid \\x escape");
  			Py_DECREF(v);
  			return NULL;
--- 1174,1179 ----
  				break;
  			}
! 			PyErr_SetString(PyExc_ValueError, 
! 					"invalid \\x escape");
  			Py_DECREF(v);
  			return NULL;
***************
*** 2648,2652 ****
  				PyTuple_SET_ITEM(tup, (i-3)/2, 
  					PyString_FromString(STR(
! 							CHILD(CHILD(n, i), 0))));
  			}
  		}
--- 2649,2653 ----
  				PyTuple_SET_ITEM(tup, (i-3)/2, 
  					PyString_FromString(STR(
! 						CHILD(CHILD(n, i), 0))));
  			}
  		}
***************
*** 3986,3990 ****
  /* Helper function to issue symbol table warnings */
  
! static void
  symtable_warn(struct symtable *st, char *msg)
  {
--- 3987,3991 ----
  /* Helper function to issue symbol table warnings */
  
! static int
  symtable_warn(struct symtable *st, char *msg)
  {
***************
*** 3997,4001 ****
--- 3998,4004 ----
  		}
  		st->st_errors++;
+ 		return -1;
  	}
+ 	return 0;
  }
  
***************
*** 4121,4124 ****
--- 4124,4219 ----
  
  static int
+ symtable_check_unoptimized(struct compiling *c,
+ 			   PySymtableEntryObject *ste, 
+ 			   struct symbol_info *si)
+ {
+ 	char buf[300];
+ 
+ 	if (!(si->si_ncells || si->si_nfrees || ste->ste_child_free
+ 	      || (ste->ste_nested && si->si_nimplicit)))
+ 		return 0;
+ 
+ #define ILLEGAL_IMPORT_STAR \
+ "import * is not allowed in function '%.100s' " \
+ "because it contains a nested function with free variables"
+ 
+ #define ILLEGAL_BARE_EXEC \
+ "unqualified exec is not allowed in function '%.100s' " \
+ "because it contains a nested function with free variables"
+ 
+ #define ILLEGAL_EXEC_AND_IMPORT_STAR \
+ "function '%.100s' uses import * and bare exec, which are illegal" \
+ "because it contains a nested function with free variables"
+ 
+ 	/* XXX perhaps the linenos for these opt-breaking statements
+ 	   should be stored so the exception can point to them. */
+ 
+ 	if (ste->ste_optimized == OPT_IMPORT_STAR)
+ 		sprintf(buf, ILLEGAL_IMPORT_STAR, 
+ 			PyString_AS_STRING(ste->ste_name));
+ 	else if (ste->ste_optimized == (OPT_BARE_EXEC | OPT_EXEC))
+ 		sprintf(buf, ILLEGAL_BARE_EXEC,
+ 			PyString_AS_STRING(ste->ste_name));
+ 	else {
+ 		sprintf(buf, ILLEGAL_EXEC_AND_IMPORT_STAR,
+ 			PyString_AS_STRING(ste->ste_name));
+ 	}
+ 	
+ 	if (c->c_symtable->st_nested_scopes) {
+ 		PyErr_SetString(PyExc_SyntaxError, buf);
+ 		PyErr_SyntaxLocation(c->c_symtable->st_filename,
+ 				     ste->ste_lineno);
+ 		return -1;
+ 	} else {
+ 		/* XXX if the warning becomes an exception, we should
+ 		   attached more info to it. */
+ 		if (PyErr_Warn(PyExc_SyntaxWarning, buf) < 0)
+ 			return -1;
+ 	}
+ 	return 0;
+ }
+ 
+ static int
+ symtable_check_shadow(struct symtable *st, PyObject *name, int flags)
+ {
+ 	char buf[500];
+ 	PyObject *children, *v;
+ 	PySymtableEntryObject *child;
+ 	int i;
+ 
+ 	if (!(flags & DEF_BOUND))
+ 		return 0;
+ 	/* The semantics of this code will change with nested scopes.
+ 	   It is defined in the current scope and referenced in a
+ 	   child scope.  Under the old rules, the child will see a
+ 	   global.  Under the new rules, the child will see the
+ 	   binding in the current scope.
+ 	*/
+ 
+ 	/* Find name of child function that has free variable */
+ 	children = st->st_cur->ste_children;
+ 	for (i = 0; i < PyList_GET_SIZE(children); i++) {
+ 		int cflags;
+ 		child = (PySymtableEntryObject *)PyList_GET_ITEM(children, i);
+ 		v = PyDict_GetItem(child->ste_symbols, name);
+ 		if (v == NULL)
+ 			continue;
+ 		cflags = PyInt_AS_LONG(v);
+ 		if (!(cflags & DEF_BOUND))
+ 			break;
+ 	}
+ 	
+ 	sprintf(buf, "local name '%.100s' in '%.100s' shadows "
+ 		"use of '%.100s' as global in nested scope '%.100s'",
+ 		PyString_AS_STRING(name),
+ 		PyString_AS_STRING(st->st_cur->ste_name),
+ 		PyString_AS_STRING(name),
+ 		PyString_AS_STRING(child->ste_name)
+ 		);
+ 
+ 	return symtable_warn(st, buf);
+ }
+ 
+ static int
  symtable_update_flags(struct compiling *c, PySymtableEntryObject *ste,
  		      struct symbol_info *si)
***************
*** 4130,4153 ****
  		if (ste->ste_optimized == 0)
  			c->c_flags |= CO_OPTIMIZED;
! 		else if (si->si_ncells || si->si_nfrees 
! 			 || (ste->ste_nested && si->si_nimplicit)
! 			 || ste->ste_child_free) {
! 			if (c->c_symtable->st_nested_scopes) {
! 				PyErr_Format(PyExc_SyntaxError,
! 					     ILLEGAL_DYNAMIC_SCOPE, 
! 				     PyString_AS_STRING(ste->ste_name));
! 				PyErr_SyntaxLocation(c->c_symtable->st_filename,
! 						   ste->ste_lineno);
! 				return -1;
! 			} else {
! 				char buf[200];
! 				sprintf(buf, ILLEGAL_DYNAMIC_SCOPE,
! 					PyString_AS_STRING(ste->ste_name));
! 				if (PyErr_Warn(PyExc_SyntaxWarning,
! 					       buf) < 0) {
! 					return -1;
! 				}
! 			}
! 		}
  	}
  	return 0;
--- 4225,4230 ----
  		if (ste->ste_optimized == 0)
  			c->c_flags |= CO_OPTIMIZED;
! 		else if (ste->ste_optimized != OPT_EXEC) 
! 			return symtable_check_unoptimized(c, ste, si);
  	}
  	return 0;
***************
*** 4192,4195 ****
--- 4269,4278 ----
  		flags = PyInt_AS_LONG(v);
  
+ 		if (st->st_nested_scopes == 0 
+ 		    && (flags & (DEF_FREE | DEF_FREE_CLASS))) {
+ 			if (symtable_check_shadow(st, name, flags) < 0)
+ 				goto fail;
+ 		}
+ 
  		if (flags & DEF_FREE_GLOBAL)
  			/* undo the original DEF_FREE */
***************
*** 4341,4344 ****
--- 4424,4437 ----
  			}
  		}
+ /*
+ 		if (st->st_nested_scopes == 0 
+ 		    && list && PyList_GET_SIZE(list) > 0) {
+ 			fprintf(stderr, "function %s has children with "
+ 				"the following free vars:\n%s\n",
+ 				PyString_AS_STRING(ste->ste_name),
+ 				PyObject_REPR(list));
+ 			continue; 
+ 		}
+ */
  		for (j = 0; list && j < PyList_GET_SIZE(list); j++) {
  			name = PyList_GET_ITEM(list, j);
***************
*** 4433,4437 ****
  	int end;
  
! 	if (st->st_pass == 1 && st->st_nested_scopes)
  		symtable_update_free_vars(st);
  	Py_DECREF(st->st_cur);
--- 4526,4530 ----
  	int end;
  
! 	if (st->st_pass == 1)
  		symtable_update_free_vars(st);
  	Py_DECREF(st->st_cur);