Commit 151d1c84 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge branch 'nonzero'

parents eae111dd c27db779
...@@ -372,6 +372,8 @@ private: ...@@ -372,6 +372,8 @@ private:
return UNKNOWN; return UNKNOWN;
case AST_LangPrimitive::NONE: case AST_LangPrimitive::NONE:
return NONE; return NONE;
case AST_LangPrimitive::NONZERO:
return BOOL;
default: default:
RELEASE_ASSERT(0, "%d", node->opcode); RELEASE_ASSERT(0, "%d", node->opcode);
} }
......
...@@ -403,7 +403,10 @@ Value ASTInterpreter::visit_slice(AST_Slice* node) { ...@@ -403,7 +403,10 @@ Value ASTInterpreter::visit_slice(AST_Slice* node) {
} }
Value ASTInterpreter::visit_branch(AST_Branch* node) { Value ASTInterpreter::visit_branch(AST_Branch* node) {
if (nonzero(visit_expr(node->test).o)) Value v = visit_expr(node->test);
ASSERT(v.o == True || v.o == False, "Should have called NONZERO before this branch");
if (v.o == True)
next_block = node->iftrue; next_block = node->iftrue;
else else
next_block = node->iffalse; next_block = node->iffalse;
...@@ -441,6 +444,8 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) { ...@@ -441,6 +444,8 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
if (phis->isPotentiallyUndefinedAfter(name, current_block)) { if (phis->isPotentiallyUndefinedAfter(name, current_block)) {
bool is_defined = it != sym_table.end(); bool is_defined = it != sym_table.end();
sorted_symbol_table[getIsDefinedName(name)] = (Box*)is_defined; sorted_symbol_table[getIsDefinedName(name)] = (Box*)is_defined;
if (is_defined)
assert(it->getValue() != NULL);
sorted_symbol_table[name] = is_defined ? it->getValue() : NULL; sorted_symbol_table[name] = is_defined ? it->getValue() : NULL;
} else { } else {
ASSERT(it != sym_table.end(), "%s", name.c_str()); ASSERT(it != sym_table.end(), "%s", name.c_str());
...@@ -567,6 +572,7 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) { ...@@ -567,6 +572,7 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
v = boxBool(isinstance(obj.o, cls.o, unboxInt(flags.o))); v = boxBool(isinstance(obj.o, cls.o, unboxInt(flags.o)));
} else if (node->opcode == AST_LangPrimitive::LOCALS) { } else if (node->opcode == AST_LangPrimitive::LOCALS) {
assert(node->args.size() == 0);
BoxedDict* dict = new BoxedDict; BoxedDict* dict = new BoxedDict;
for (auto& p : sym_table) { for (auto& p : sym_table) {
llvm::StringRef s = p.first(); llvm::StringRef s = p.first();
...@@ -576,6 +582,10 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) { ...@@ -576,6 +582,10 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
dict->d[new BoxedString(s.str())] = p.second; dict->d[new BoxedString(s.str())] = p.second;
} }
v = dict; v = dict;
} else if (node->opcode == AST_LangPrimitive::NONZERO) {
assert(node->args.size() == 1);
Value obj = visit_expr(node->args[0]);
v = boxBool(nonzero(obj.o));
} else } else
RELEASE_ASSERT(0, "not implemented"); RELEASE_ASSERT(0, "not implemented");
return v; return v;
...@@ -735,8 +745,13 @@ Value ASTInterpreter::visit_raise(AST_Raise* node) { ...@@ -735,8 +745,13 @@ Value ASTInterpreter::visit_raise(AST_Raise* node) {
} }
Value ASTInterpreter::visit_assert(AST_Assert* node) { Value ASTInterpreter::visit_assert(AST_Assert* node) {
if (!nonzero(visit_expr(node->test).o)) #ifndef NDEBUG
assertFail(source_info->parent_module, node->msg ? visit_expr(node->msg).o : 0); // Currently we only generate "assert 0" statements
Value v = visit_expr(node->test);
assert(v.o->cls == int_cls && static_cast<BoxedInt*>(v.o)->n == 0);
#endif
assertFail(source_info->parent_module, node->msg ? visit_expr(node->msg).o : 0);
return Value(); return Value();
} }
...@@ -1002,8 +1017,6 @@ Value ASTInterpreter::visit_name(AST_Name* node) { ...@@ -1002,8 +1017,6 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
SymMap::iterator it = sym_table.find(node->id); SymMap::iterator it = sym_table.find(node->id);
if (it != sym_table.end()) { if (it != sym_table.end()) {
Box* value = it->second; Box* value = it->second;
if (!value)
assertNameDefined(value, node->id.c_str(), UnboundLocalError, true);
return value; return value;
} }
......
...@@ -752,7 +752,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua ...@@ -752,7 +752,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
// TODO: inneficient // TODO: inneficient
sym_table = new SymbolTable(*sym_table); sym_table = new SymbolTable(*sym_table);
assert(sym_table->count(name->id)); ASSERT(sym_table->count(name->id), "%d %s\n", block->idx, name->id.c_str());
sym_table->erase(name->id); sym_table->erase(name->id);
created_new_sym_table = true; created_new_sym_table = true;
} }
......
...@@ -543,6 +543,17 @@ private: ...@@ -543,6 +543,17 @@ private:
case AST_LangPrimitive::NONE: { case AST_LangPrimitive::NONE: {
return getNone(); return getNone();
} }
case AST_LangPrimitive::NONZERO: {
assert(node->args.size() == 1);
CompilerVariable* obj = evalExpr(node->args[0], unw_info);
ConcreteCompilerVariable* rtn = obj->nonzero(emitter, getOpInfoForNode(node, unw_info));
assert(rtn->getType() == BOOL);
llvm::Value* v = i1FromBool(emitter, rtn);
assert(v->getType() == g.i1);
return boolFromI1(emitter, v);
}
default: default:
RELEASE_ASSERT(0, "%d", node->opcode); RELEASE_ASSERT(0, "%d", node->opcode);
} }
...@@ -1812,22 +1823,18 @@ private: ...@@ -1812,22 +1823,18 @@ private:
assert(state != PARTIAL); assert(state != PARTIAL);
assert(val); assert(val);
// ASSERT(val->getType() == BOOL, "%s", val->getType()->debugName().c_str()); // We could call nonzero here if there is no try-catch block?
ASSERT(val->getType() == BOOL, "should have called NONZERO before this; is %s",
val->getType()->debugName().c_str());
llvm::Value* v = i1FromBool(emitter, static_cast<ConcreteCompilerVariable*>(val));
assert(v->getType() == g.i1);
ConcreteCompilerVariable* nonzero = val->nonzero(emitter, getOpInfoForNode(node, unw_info));
ASSERT(nonzero->getType() == BOOL, "%s %s", val->getType()->debugName().c_str(),
nonzero->getType()->debugName().c_str());
val->decvref(emitter);
llvm::Value* llvm_nonzero = i1FromBool(emitter, nonzero);
llvm::BasicBlock* iftrue = entry_blocks[node->iftrue]; llvm::BasicBlock* iftrue = entry_blocks[node->iftrue];
llvm::BasicBlock* iffalse = entry_blocks[node->iffalse]; llvm::BasicBlock* iffalse = entry_blocks[node->iffalse];
nonzero->decvref(emitter);
endBlock(FINISHED); endBlock(FINISHED);
emitter.getBuilder()->CreateCondBr(llvm_nonzero, iftrue, iffalse); emitter.getBuilder()->CreateCondBr(v, iftrue, iffalse);
} }
void doExpr(AST_Expr* node, UnwindInfo unw_info) { void doExpr(AST_Expr* node, UnwindInfo unw_info) {
......
...@@ -24,6 +24,10 @@ ...@@ -24,6 +24,10 @@
namespace pyston { namespace pyston {
#ifndef NDEBUG
int AST::next_lineno = 100000;
#endif
llvm::StringRef getOpSymbol(int op_type) { llvm::StringRef getOpSymbol(int op_type) {
switch (op_type) { switch (op_type) {
case AST_TYPE::Add: case AST_TYPE::Add:
...@@ -1448,6 +1452,9 @@ bool PrintVisitor::visit_langprimitive(AST_LangPrimitive* node) { ...@@ -1448,6 +1452,9 @@ bool PrintVisitor::visit_langprimitive(AST_LangPrimitive* node) {
case AST_LangPrimitive::NONE: case AST_LangPrimitive::NONE:
printf("NONE"); printf("NONE");
break; break;
case AST_LangPrimitive::NONZERO:
printf("NONZERO");
break;
default: default:
RELEASE_ASSERT(0, "%d", node->opcode); RELEASE_ASSERT(0, "%d", node->opcode);
} }
......
...@@ -148,7 +148,17 @@ public: ...@@ -148,7 +148,17 @@ public:
virtual void accept(ASTVisitor* v) = 0; virtual void accept(ASTVisitor* v) = 0;
#ifndef NDEBUG
private:
static int next_lineno;
public:
// In debug mode, initialize lineno to something unique, so that if we see something ridiculous
// appear in the traceback, we can isolate the allocation which created it.
AST(AST_TYPE::AST_TYPE type) : type(type), lineno(++next_lineno) {}
#else
AST(AST_TYPE::AST_TYPE type) : type(type) {} AST(AST_TYPE::AST_TYPE type) : type(type) {}
#endif
}; };
class AST_expr : public AST { class AST_expr : public AST {
...@@ -966,6 +976,7 @@ public: ...@@ -966,6 +976,7 @@ public:
IMPORT_NAME, IMPORT_NAME,
IMPORT_STAR, IMPORT_STAR,
NONE, NONE,
NONZERO,
} opcode; } opcode;
std::vector<AST_expr*> args; std::vector<AST_expr*> args;
......
...@@ -131,6 +131,22 @@ private: ...@@ -131,6 +131,22 @@ private:
return NULL; return NULL;
} }
AST_expr* callNonzero(AST_expr* e) {
AST_LangPrimitive* call = new AST_LangPrimitive(AST_LangPrimitive::NONZERO);
call->args.push_back(e);
call->lineno = e->lineno;
call->col_offset = e->col_offset;
// Simple optimization: allow the generation of nested nodes if there isn't a
// current exc handler.
if (exc_handlers.size() == 0)
return call;
auto name = nodeName(e);
pushAssign(name, call);
return makeName(name, AST_TYPE::Load);
}
AST_expr* applyComprehensionCall(AST_DictComp* node, AST_Name* name) { AST_expr* applyComprehensionCall(AST_DictComp* node, AST_Name* name) {
AST_expr* key = remapExpr(node->key); AST_expr* key = remapExpr(node->key);
AST_expr* value = remapExpr(node->value); AST_expr* value = remapExpr(node->value);
...@@ -179,7 +195,7 @@ private: ...@@ -179,7 +195,7 @@ private:
push_back(j); push_back(j);
curblock = test_block; curblock = test_block;
AST_expr* test_call = remapExpr(makeCall(hasnext_attr)); AST_expr* test_call = callNonzero(remapExpr(makeCall(hasnext_attr)));
CFGBlock* body_block = cfg->addBlock(); CFGBlock* body_block = cfg->addBlock();
body_block->info = "comprehension_body"; body_block->info = "comprehension_body";
...@@ -204,7 +220,7 @@ private: ...@@ -204,7 +220,7 @@ private:
pushAssign(c->target, makeName(next_name, AST_TYPE::Load)); pushAssign(c->target, makeName(next_name, AST_TYPE::Load));
for (AST_expr* if_condition : c->ifs) { for (AST_expr* if_condition : c->ifs) {
AST_expr* remapped = remapExpr(if_condition); AST_expr* remapped = callNonzero(remapExpr(if_condition));
AST_Branch* br = new AST_Branch(); AST_Branch* br = new AST_Branch();
br->test = remapped; br->test = remapped;
push_back(br); push_back(br);
...@@ -284,7 +300,7 @@ private: ...@@ -284,7 +300,7 @@ private:
AST_Branch* makeBranch(AST_expr* test) { AST_Branch* makeBranch(AST_expr* test) {
AST_Branch* rtn = new AST_Branch(); AST_Branch* rtn = new AST_Branch();
rtn->test = test; rtn->test = callNonzero(test);
rtn->col_offset = test->col_offset; rtn->col_offset = test->col_offset;
rtn->lineno = test->lineno; rtn->lineno = test->lineno;
return rtn; return rtn;
...@@ -520,7 +536,7 @@ private: ...@@ -520,7 +536,7 @@ private:
pushAssign(name, val); pushAssign(name, val);
AST_Branch* br = new AST_Branch(); AST_Branch* br = new AST_Branch();
br->test = _dup(val); br->test = callNonzero(_dup(val));
push_back(br); push_back(br);
CFGBlock* was_block = curblock; CFGBlock* was_block = curblock;
...@@ -637,7 +653,7 @@ private: ...@@ -637,7 +653,7 @@ private:
pushAssign(name, val); pushAssign(name, val);
AST_Branch* br = new AST_Branch(); AST_Branch* br = new AST_Branch();
br->test = makeName(name, AST_TYPE::Load); br->test = callNonzero(makeName(name, AST_TYPE::Load));
push_back(br); push_back(br);
CFGBlock* was_block = curblock; CFGBlock* was_block = curblock;
...@@ -725,6 +741,9 @@ private: ...@@ -725,6 +741,9 @@ private:
for (AST_expr* if_condition : c->ifs) { for (AST_expr* if_condition : c->ifs) {
AST_If* if_block = new AST_If(); AST_If* if_block = new AST_If();
// Note: don't call callNonzero here, since we are generating
// AST inside a new functiondef which will go through the CFG
// process again.
if_block->test = if_condition; if_block->test = if_condition;
insert_point->push_back(if_block); insert_point->push_back(if_block);
...@@ -751,13 +770,14 @@ private: ...@@ -751,13 +770,14 @@ private:
AST_expr* remapIfExp(AST_IfExp* node) { AST_expr* remapIfExp(AST_IfExp* node) {
std::string rtn_name = nodeName(node); std::string rtn_name = nodeName(node);
CFGBlock* starting_block = curblock;
AST_Branch* br = new AST_Branch(); AST_Branch* br = new AST_Branch();
br->col_offset = node->col_offset; br->col_offset = node->col_offset;
br->lineno = node->lineno; br->lineno = node->lineno;
br->test = remapExpr(node->test); br->test = callNonzero(remapExpr(node->test));
push_back(br); push_back(br);
CFGBlock* starting_block = curblock;
CFGBlock* iftrue = cfg->addBlock(); CFGBlock* iftrue = cfg->addBlock();
iftrue->info = "iftrue"; iftrue->info = "iftrue";
br->iftrue = iftrue; br->iftrue = iftrue;
...@@ -1055,6 +1075,10 @@ public: ...@@ -1055,6 +1075,10 @@ public:
#endif #endif
curblock->push_back(node); curblock->push_back(node);
return; return;
} else if (asgn->value->type == AST_TYPE::Name && ast_cast<AST_Name>(asgn->value)->id[0] == '#') {
// Assigning from one temporary name to another:
curblock->push_back(node);
return;
} }
} }
} }
...@@ -1246,7 +1270,7 @@ public: ...@@ -1246,7 +1270,7 @@ public:
bool visit_assert(AST_Assert* node) override { bool visit_assert(AST_Assert* node) override {
AST_Branch* br = new AST_Branch(); AST_Branch* br = new AST_Branch();
br->test = remapExpr(node->test); br->test = callNonzero(remapExpr(node->test));
push_back(br); push_back(br);
CFGBlock* iffalse = cfg->addBlock(); CFGBlock* iffalse = cfg->addBlock();
...@@ -1498,7 +1522,7 @@ public: ...@@ -1498,7 +1522,7 @@ public:
AST_Branch* br = new AST_Branch(); AST_Branch* br = new AST_Branch();
br->col_offset = node->col_offset; br->col_offset = node->col_offset;
br->lineno = node->lineno; br->lineno = node->lineno;
br->test = remapExpr(node->test); br->test = callNonzero(remapExpr(node->test));
push_back(br); push_back(br);
CFGBlock* starting_block = curblock; CFGBlock* starting_block = curblock;
...@@ -1827,7 +1851,7 @@ public: ...@@ -1827,7 +1851,7 @@ public:
is_caught_here->args.push_back(makeNum(1)); // flag: false_on_noncls is_caught_here->args.push_back(makeNum(1)); // flag: false_on_noncls
AST_Branch* br = new AST_Branch(); AST_Branch* br = new AST_Branch();
br->test = remapExpr(is_caught_here); br->test = callNonzero(remapExpr(is_caught_here));
CFGBlock* exc_handle = cfg->addBlock(); CFGBlock* exc_handle = cfg->addBlock();
exc_next = cfg->addDeferredBlock(); exc_next = cfg->addDeferredBlock();
...@@ -2153,8 +2177,18 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) { ...@@ -2153,8 +2177,18 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
for (auto b : rtn->blocks) for (auto b : rtn->blocks)
flatten(b->body, flattened, true); flatten(b->body, flattened, true);
std::unordered_set<AST*> deduped(flattened.begin(), flattened.end()); std::unordered_map<AST*, int> deduped;
assert(deduped.size() == flattened.size()); bool no_dups = true;
for (auto e : flattened) {
deduped[e]++;
if (deduped[e] == 2) {
printf("Duplicated: ");
print_ast(e);
printf("\n");
no_dups = false;
}
}
assert(no_dups);
// TODO make sure the result of Invoke nodes are not used on the exceptional path // TODO make sure the result of Invoke nodes are not used on the exceptional path
#endif #endif
......
...@@ -1631,6 +1631,8 @@ bool isUserDefined(BoxedClass* cls) { ...@@ -1631,6 +1631,8 @@ bool isUserDefined(BoxedClass* cls) {
} }
extern "C" bool nonzero(Box* obj) { extern "C" bool nonzero(Box* obj) {
assert(gc::isValidGCObject(obj));
static StatCounter slowpath_nonzero("slowpath_nonzero"); static StatCounter slowpath_nonzero("slowpath_nonzero");
std::unique_ptr<Rewriter> rewriter( std::unique_ptr<Rewriter> rewriter(
...@@ -1689,6 +1691,8 @@ extern "C" bool nonzero(Box* obj) { ...@@ -1689,6 +1691,8 @@ extern "C" bool nonzero(Box* obj) {
// go through descriptor logic // go through descriptor logic
Box* func = getclsattr_internal(obj, "__nonzero__", NULL); Box* func = getclsattr_internal(obj, "__nonzero__", NULL);
if (!func)
func = getclsattr_internal(obj, "__len__", NULL);
if (func == NULL) { if (func == NULL) {
ASSERT(isUserDefined(obj->cls) || obj->cls == classobj_cls, "%s.__nonzero__", ASSERT(isUserDefined(obj->cls) || obj->cls == classobj_cls, "%s.__nonzero__",
...@@ -1697,6 +1701,7 @@ extern "C" bool nonzero(Box* obj) { ...@@ -1697,6 +1701,7 @@ extern "C" bool nonzero(Box* obj) {
} }
Box* r = runtimeCall0(func, ArgPassSpec(0)); Box* r = runtimeCall0(func, ArgPassSpec(0));
// I believe this behavior is handled by the slot wrappers in CPython:
if (r->cls == bool_cls) { if (r->cls == bool_cls) {
BoxedBool* b = static_cast<BoxedBool*>(r); BoxedBool* b = static_cast<BoxedBool*>(r);
bool rtn = b->n; bool rtn = b->n;
......
...@@ -34,4 +34,16 @@ if 0: ...@@ -34,4 +34,16 @@ if 0:
print bool(c) # this will fail print bool(c) # this will fail
print 1 and c # Note: nonzero isn't called on the second argument! print 1 and c # Note: nonzero isn't called on the second argument!
print C(True) or 1 # prints the object repr, not the nonzero repr print C(True) or 1 # prints the object repr, not the nonzero repr
print c and 1
print
# nonzero should fall back on __len__ if that exists but __nonzero__ doesn't:
class D(object):
def __init__(self, n):
self.n = n
def __len__(self):
print "__len__"
return self.n
for i in xrange(0, 3):
print i, bool(D(i))
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
# Exception-catching is supposed to go through __subclasscheck__ # Exception-catching is supposed to go through __subclasscheck__
class MyException(Exception):
pass
class M(type): class M(type):
def __instancecheck__(self, instance): def __instancecheck__(self, instance):
print "instancecheck", instance print "instancecheck", instance
...@@ -10,10 +13,19 @@ class M(type): ...@@ -10,10 +13,19 @@ class M(type):
def __subclasscheck__(self, sub): def __subclasscheck__(self, sub):
print "subclasscheck", sub print "subclasscheck", sub
if self.throw_on_subclasscheck:
raise MyException()
return True return True
class E(Exception): class E(Exception):
__metaclass__ = M __metaclass__ = M
throw_on_subclasscheck = False
class F(Exception):
__metaclass__ = M
throw_on_subclasscheck = True
print 1 print 1
print isinstance(E(), E) # does not print anything due to special-casing print isinstance(E(), E) # does not print anything due to special-casing
...@@ -35,3 +47,12 @@ except E: ...@@ -35,3 +47,12 @@ except E:
pass pass
print 5 print 5
# Exceptions in __subclasscheck__ should get ignored:
try:
1/0
except F:
print "shouldn't get here"
except ZeroDivisionError:
print "ok"
class MyException(Exception):
pass
class C(object):
def __init__(self, x):
self.x = x
def __nonzero__(self):
raise MyException(self.x)
def __repr__(self):
return "<C %r>" % self.x
# Make sure that we can handle nonzero() throwing an exception wherever it occurs:
try:
print C(1) and 1
except MyException, e:
print e
try:
print C(2) or 1
except MyException, e:
print e
try:
if C(3):
pass
except MyException, e:
print e
try:
while C(4):
pass
except MyException, e:
print e
try:
assert C(5)
except MyException, e:
print e
try:
print [1 for i in range(5) if C(6)]
except MyException, e:
print e
try:
print list(1 for i in range(5) if C(7))
except MyException, e:
print e
try:
print 1 if C(8) else 0
except MyException, e:
print e
class M(type):
def __instancecheck__(self, instance):
print "instancecheck", instance
return C(9)
def __subclasscheck__(self, sub):
print "subclasscheck", sub
return C(10)
class F(Exception):
__metaclass__ = M
try:
try:
1/0
except C("a") or 1:
print "shouldn't get here"
print "shouldn't get here 2"
except MyException, e:
print e
class E(object):
def __lt__(self, rhs):
return C("false")
# This is ok because it doesn't evaluate the result of the comparison:
print E() < 1
try:
# This is not ok because it will!
print E() < 1 < 1
except MyException, e:
print e
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