Commit ec5a99c9 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #419 from tjhance/del-closure-var-syntax-error

through syntax error when you to `del` a var stored in a closure
parents 0a61896d 1798ea09
...@@ -183,6 +183,7 @@ struct ScopingAnalysis::ScopeNameUsage { ...@@ -183,6 +183,7 @@ struct ScopingAnalysis::ScopeNameUsage {
StrSet read; StrSet read;
StrSet written; StrSet written;
StrSet forced_globals; StrSet forced_globals;
std::vector<AST_Name*> del_name_nodes;
// Properties determined by looking at other scopes as well: // Properties determined by looking at other scopes as well:
StrSet referenced_from_nested; StrSet referenced_from_nested;
...@@ -190,7 +191,7 @@ struct ScopingAnalysis::ScopeNameUsage { ...@@ -190,7 +191,7 @@ struct ScopingAnalysis::ScopeNameUsage {
StrSet passthrough_accesses; // what names a child scope accesses a name from a parent scope StrSet passthrough_accesses; // what names a child scope accesses a name from a parent scope
// `import *` and `exec` both force the scope to use the NAME lookup // `import *` and `exec` both force the scope to use the NAME lookup
// However, this is not allowed to happen (a SyntaxError) if the scope // However, this is not allowed to happen (a SyntaxError) if the scope has
// "free variables", variables read but not written (and not forced to be global) // "free variables", variables read but not written (and not forced to be global)
// Furthermore, no child of the scope can have any free variables either // Furthermore, no child of the scope can have any free variables either
// (not even if the variables would refer to a closure in an in-between child). // (not even if the variables would refer to a closure in an in-between child).
...@@ -341,6 +342,8 @@ public: ...@@ -341,6 +342,8 @@ public:
cur->read.insert(name); cur->read.insert(name);
} }
void doDel(AST_Name* node) { cur->del_name_nodes.push_back(node); }
void doImportStar(AST_ImportFrom* node) { void doImportStar(AST_ImportFrom* node) {
if (cur->nameForcingNodeImportStar == NULL) if (cur->nameForcingNodeImportStar == NULL)
cur->nameForcingNodeImportStar = node; cur->nameForcingNodeImportStar = node;
...@@ -358,9 +361,11 @@ public: ...@@ -358,9 +361,11 @@ public:
case AST_TYPE::Load: case AST_TYPE::Load:
doRead(node->id); doRead(node->id);
break; break;
case AST_TYPE::Del:
doDel(node);
// fallthrough
case AST_TYPE::Param: case AST_TYPE::Param:
case AST_TYPE::Store: case AST_TYPE::Store:
case AST_TYPE::Del:
doWrite(node->id); doWrite(node->id);
break; break;
default: default:
...@@ -699,6 +704,21 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) { ...@@ -699,6 +704,21 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) {
else if (usage->free) else if (usage->free)
raiseNameForcingSyntaxError("is a nested function", usage); raiseNameForcingSyntaxError("is a nested function", usage);
} }
// Trying to `del` a varaible in the closure in a SyntaxError.
// NOTE(travis): I'm not sure why this is a syntax error;
// it doesn't seem like there is anything intrinisically difficult about supporting
// `del` for closure variables. But it is, so, there you go:
for (AST_Name* name_node : usage->del_name_nodes) {
InternedString name = name_node->id;
if (usage->referenced_from_nested.count(name) > 0) {
char buf[1024];
snprintf(buf, sizeof(buf), "can not delete variable '%s' referenced in nested scope", name.c_str());
assert(usage->node->type == AST_TYPE::FunctionDef);
AST_FunctionDef* funcNode = static_cast<AST_FunctionDef*>(usage->node);
raiseSyntaxError(buf, name_node->lineno, 0, "" /* file?? */, funcNode->name.str());
}
}
} }
std::vector<ScopeNameUsage*> sorted_usages = sortNameUsages(usages); std::vector<ScopeNameUsage*> sorted_usages = sortNameUsages(usages);
......
cases = [
"""
# should fail
def f():
a = 0
def g():
print a
del a
""", """
# should fail
def f():
def g():
print a
del a
""", """
def f():
global a
a = 0
def g():
print a
del a
""", """
def f():
a = 0
def g():
global a
print a
del a
""", """
def f():
a = 0
def g():
global a
def h():
print a
del a
""", """
def f():
class C(object):
a = 0
del a
def g():
print a
"""
]
#import traceback
for case in cases:
print case
try:
exec case
except SyntaxError as se:
print se.message
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment