Commit 8c18a760 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Fix a bug in expression evaluation order

Also, give up on maintaining 100% error message compatibility
with CPython.  There are some places where it's kind of silly
to do this since the messages seem based on internal implementation
details; namely, whether to output at the global scope:
NameError: global name 'x' is not defined
vs
NameError: name 'x' is not defined
This depends on whether any function has been defined that has
a "global x" declaration.

Instead, make the tester a little bit smarter to know that certain
classes of error messages should be treated as equivalent.
parent 09d084d1
...@@ -756,7 +756,6 @@ private: ...@@ -756,7 +756,6 @@ private:
// Method 1: calls into the runtime getGlobal(), which handles things like falling back to builtins // Method 1: calls into the runtime getGlobal(), which handles things like falling back to builtins
// or raising the correct error message. // or raising the correct error message.
bool do_patchpoint = ENABLE_ICGETGLOBALS && (irstate->getEffortLevel() != EffortLevel::INTERPRETED); bool do_patchpoint = ENABLE_ICGETGLOBALS && (irstate->getEffortLevel() != EffortLevel::INTERPRETED);
bool from_global = irstate->getSourceInfo()->ast->type == AST_TYPE::Module;
if (do_patchpoint) { if (do_patchpoint) {
PatchpointSetupInfo* pp = patchpoints::createGetGlobalPatchpoint( PatchpointSetupInfo* pp = patchpoints::createGetGlobalPatchpoint(
emitter.currentFunction(), getOpInfoForNode(node, exc_info).getTypeRecorder()); emitter.currentFunction(), getOpInfoForNode(node, exc_info).getTypeRecorder());
...@@ -765,18 +764,17 @@ private: ...@@ -765,18 +764,17 @@ private:
llvm_args.push_back( llvm_args.push_back(
embedConstantPtr(irstate->getSourceInfo()->parent_module, g.llvm_module_type_ptr)); embedConstantPtr(irstate->getSourceInfo()->parent_module, g.llvm_module_type_ptr));
llvm_args.push_back(embedConstantPtr(&node->id, g.llvm_str_type_ptr)); llvm_args.push_back(embedConstantPtr(&node->id, g.llvm_str_type_ptr));
llvm_args.push_back(getConstantInt(from_global, g.i1));
llvm::Value* uncasted llvm::Value* uncasted
= emitter.createPatchpoint(pp, (void*)pyston::getGlobal, llvm_args, exc_info).getInstruction(); = emitter.createPatchpoint(pp, (void*)pyston::getGlobal, llvm_args, exc_info).getInstruction();
llvm::Value* r = emitter.getBuilder()->CreateIntToPtr(uncasted, g.llvm_value_type_ptr); llvm::Value* r = emitter.getBuilder()->CreateIntToPtr(uncasted, g.llvm_value_type_ptr);
return new ConcreteCompilerVariable(UNKNOWN, r, true); return new ConcreteCompilerVariable(UNKNOWN, r, true);
} else { } else {
llvm::Value* r = emitter.createCall3(exc_info, g.funcs.getGlobal, llvm::Value* r
embedConstantPtr(irstate->getSourceInfo()->parent_module, = emitter.createCall2(
g.llvm_module_type_ptr), exc_info, g.funcs.getGlobal,
embedConstantPtr(&node->id, g.llvm_str_type_ptr), embedConstantPtr(irstate->getSourceInfo()->parent_module, g.llvm_module_type_ptr),
getConstantInt(from_global, g.i1)).getInstruction(); embedConstantPtr(&node->id, g.llvm_str_type_ptr)).getInstruction();
return new ConcreteCompilerVariable(UNKNOWN, r, true); return new ConcreteCompilerVariable(UNKNOWN, r, true);
} }
} else { } else {
......
...@@ -661,7 +661,8 @@ private: ...@@ -661,7 +661,8 @@ private:
rtn = remapListComp(ast_cast<AST_ListComp>(node)); rtn = remapListComp(ast_cast<AST_ListComp>(node));
break; break;
case AST_TYPE::Name: case AST_TYPE::Name:
return node; rtn = node;
break;
case AST_TYPE::Num: case AST_TYPE::Num:
return node; return node;
case AST_TYPE::Repr: case AST_TYPE::Repr:
...@@ -685,7 +686,7 @@ private: ...@@ -685,7 +686,7 @@ private:
RELEASE_ASSERT(0, "%d", node->type); RELEASE_ASSERT(0, "%d", node->type);
} }
if (wrap_with_assign && rtn->type != AST_TYPE::Name) { if (wrap_with_assign && (rtn->type != AST_TYPE::Name || ast_cast<AST_Name>(rtn)->id[0] != '#')) {
std::string name = nodeName(node); std::string name = nodeName(node);
push_back(makeAssign(name, rtn)); push_back(makeAssign(name, rtn));
return makeName(name, AST_TYPE::Load); return makeName(name, AST_TYPE::Load);
......
...@@ -2362,7 +2362,7 @@ Box* typeNew(Box* cls, Box* obj) { ...@@ -2362,7 +2362,7 @@ Box* typeNew(Box* cls, Box* obj) {
return rtn; return rtn;
} }
extern "C" Box* getGlobal(BoxedModule* m, std::string* name, bool from_global) { extern "C" Box* getGlobal(BoxedModule* m, std::string* name) {
static StatCounter slowpath_getglobal("slowpath_getglobal"); static StatCounter slowpath_getglobal("slowpath_getglobal");
slowpath_getglobal.log(); slowpath_getglobal.log();
static StatCounter nopatch_getglobal("nopatch_getglobal"); static StatCounter nopatch_getglobal("nopatch_getglobal");
...@@ -2429,10 +2429,7 @@ extern "C" Box* getGlobal(BoxedModule* m, std::string* name, bool from_global) { ...@@ -2429,10 +2429,7 @@ extern "C" Box* getGlobal(BoxedModule* m, std::string* name, bool from_global) {
return rtn; return rtn;
} }
if (from_global) raiseExcHelper(NameError, "global name '%s' is not defined", name->c_str());
raiseExcHelper(NameError, "name '%s' is not defined", name->c_str());
else
raiseExcHelper(NameError, "global name '%s' is not defined", name->c_str());
} }
// TODO I feel like importing should go somewhere else; it's more closely tied to codegen // TODO I feel like importing should go somewhere else; it's more closely tied to codegen
......
...@@ -57,7 +57,7 @@ extern "C" void dump(Box* obj); ...@@ -57,7 +57,7 @@ extern "C" void dump(Box* obj);
extern "C" i64 unboxedLen(Box* obj); extern "C" i64 unboxedLen(Box* obj);
extern "C" Box* binop(Box* lhs, Box* rhs, int op_type); extern "C" Box* binop(Box* lhs, Box* rhs, int op_type);
extern "C" Box* augbinop(Box* lhs, Box* rhs, int op_type); extern "C" Box* augbinop(Box* lhs, Box* rhs, int op_type);
extern "C" Box* getGlobal(BoxedModule* m, std::string* name, bool from_global); extern "C" Box* getGlobal(BoxedModule* m, std::string* name);
extern "C" Box* getitem(Box* value, Box* slice); extern "C" Box* getitem(Box* value, Box* slice);
extern "C" void setitem(Box* target, Box* slice, Box* value); extern "C" void setitem(Box* target, Box* slice, Box* value);
extern "C" Box* getclsattr(Box* obj, const char* attr); extern "C" Box* getclsattr(Box* obj, const char* attr);
......
# The function should get evaluated before the arguments
# This is especially tricky if the evaluation of the function
# expression can change the behavior of the argument expressions.
def f1():
global g1
def g1(x):
return x
def inner(x):
return x
return inner
print f1()(g1(1))
def f2(y):
global g2
def g2(x):
return x
return y
print g2(f2(2))
...@@ -72,12 +72,32 @@ def get_expected_output(fn): ...@@ -72,12 +72,32 @@ def get_expected_output(fn):
out, err = p.communicate() out, err = p.communicate()
code = p.wait() code = p.wait()
r = code, out, err.strip().split('\n')[-1] r = code, out, err
assert code >= 0, "CPython exited with an unexpected exit code: %d" % (code,) assert code >= 0, "CPython exited with an unexpected exit code: %d" % (code,)
cPickle.dump(r, open(cache_fn, 'w')) cPickle.dump(r, open(cache_fn, 'w'))
return r return r
def canonicalize_stderr(stderr):
"""
For a while we were trying to maintain *exact* stderr compatibility with CPython,
at least for the last line of the stderr.
It was starting to get silly to do this, so instead apply some "canonicalizations"
to map certain groups of error messages together.
"""
stderr = stderr.strip().split('\n')[-1]
substitutions = [
("NameError: global name '", "NameError: name '"),
]
for pattern, subst_with in substitutions:
stderr = stderr.replace(pattern, subst_with)
return stderr
failed = [] failed = []
def run_test(fn, check_stats, run_memcheck): def run_test(fn, check_stats, run_memcheck):
r = fn.rjust(FN_JUST_SIZE) r = fn.rjust(FN_JUST_SIZE)
...@@ -107,9 +127,8 @@ def run_test(fn, check_stats, run_memcheck): ...@@ -107,9 +127,8 @@ def run_test(fn, check_stats, run_memcheck):
run_args = [os.path.abspath(IMAGE)] + jit_args + [fn] run_args = [os.path.abspath(IMAGE)] + jit_args + [fn]
start = time.time() start = time.time()
p = subprocess.Popen(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=open("/dev/null"), preexec_fn=set_ulimits) p = subprocess.Popen(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=open("/dev/null"), preexec_fn=set_ulimits)
out, err = p.communicate() out, stderr = p.communicate()
full_err = err last_stderr_line = stderr.strip().split('\n')[-1]
err = err.strip().split('\n')[-1]
code = p.wait() code = p.wait()
elapsed = time.time() - start elapsed = time.time() - start
...@@ -128,6 +147,8 @@ def run_test(fn, check_stats, run_memcheck): ...@@ -128,6 +147,8 @@ def run_test(fn, check_stats, run_memcheck):
if code == 0: if code == 0:
err = "(Unexpected success)" err = "(Unexpected success)"
else:
err = last_stderr_line
if code == -signal.SIGALRM: if code == -signal.SIGALRM:
msg = "Timed out" msg = "Timed out"
...@@ -146,7 +167,7 @@ def run_test(fn, check_stats, run_memcheck): ...@@ -146,7 +167,7 @@ def run_test(fn, check_stats, run_memcheck):
failed.append(fn) failed.append(fn)
return r return r
else: else:
raise Exception("%s\n%s\n%s" % (msg, err, full_err)) raise Exception("%s\n%s\n%s" % (msg, err, stderr))
elif out != expected_out: elif out != expected_out:
if expected == "fail": if expected == "fail":
r += " Expected failure (bad output)" r += " Expected failure (bad output)"
...@@ -164,13 +185,13 @@ def run_test(fn, check_stats, run_memcheck): ...@@ -164,13 +185,13 @@ def run_test(fn, check_stats, run_memcheck):
diff = p.stdout.read() diff = p.stdout.read()
assert p.wait() in (0, 1) assert p.wait() in (0, 1)
raise Exception("Failed on %s:\n%s" % (fn, diff)) raise Exception("Failed on %s:\n%s" % (fn, diff))
elif not TEST_PYPY and err != expected_err: elif not TEST_PYPY and canonicalize_stderr(stderr) != canonicalize_stderr(expected_err):
if KEEP_GOING: if KEEP_GOING:
r += " \033[31mFAILED\033[0m (bad stderr)" r += " \033[31mFAILED\033[0m (bad stderr)"
failed.append(fn) failed.append(fn)
return r return r
else: else:
raise Exception((err, expected_err)) raise Exception((canonicalize_stderr(stderr), canonicalize_stderr(expected_err)))
elif expected == "fail": elif expected == "fail":
if KEEP_GOING: if KEEP_GOING:
r += " \033[31mFAILED\033[0m (unexpected success)" r += " \033[31mFAILED\033[0m (unexpected success)"
......
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