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

Merge branch 'nonzero'

parents eae111dd c27db779
......@@ -372,6 +372,8 @@ private:
return UNKNOWN;
case AST_LangPrimitive::NONE:
return NONE;
case AST_LangPrimitive::NONZERO:
return BOOL;
default:
RELEASE_ASSERT(0, "%d", node->opcode);
}
......
......@@ -403,7 +403,10 @@ Value ASTInterpreter::visit_slice(AST_Slice* 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;
else
next_block = node->iffalse;
......@@ -441,6 +444,8 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
if (phis->isPotentiallyUndefinedAfter(name, current_block)) {
bool is_defined = it != sym_table.end();
sorted_symbol_table[getIsDefinedName(name)] = (Box*)is_defined;
if (is_defined)
assert(it->getValue() != NULL);
sorted_symbol_table[name] = is_defined ? it->getValue() : NULL;
} else {
ASSERT(it != sym_table.end(), "%s", name.c_str());
......@@ -567,6 +572,7 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
v = boxBool(isinstance(obj.o, cls.o, unboxInt(flags.o)));
} else if (node->opcode == AST_LangPrimitive::LOCALS) {
assert(node->args.size() == 0);
BoxedDict* dict = new BoxedDict;
for (auto& p : sym_table) {
llvm::StringRef s = p.first();
......@@ -576,6 +582,10 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
dict->d[new BoxedString(s.str())] = p.second;
}
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
RELEASE_ASSERT(0, "not implemented");
return v;
......@@ -735,8 +745,13 @@ Value ASTInterpreter::visit_raise(AST_Raise* node) {
}
Value ASTInterpreter::visit_assert(AST_Assert* node) {
if (!nonzero(visit_expr(node->test).o))
assertFail(source_info->parent_module, node->msg ? visit_expr(node->msg).o : 0);
#ifndef NDEBUG
// 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();
}
......@@ -1002,8 +1017,6 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
SymMap::iterator it = sym_table.find(node->id);
if (it != sym_table.end()) {
Box* value = it->second;
if (!value)
assertNameDefined(value, node->id.c_str(), UnboundLocalError, true);
return value;
}
......
......@@ -752,7 +752,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
// TODO: inneficient
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);
created_new_sym_table = true;
}
......
......@@ -543,6 +543,17 @@ private:
case AST_LangPrimitive::NONE: {
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:
RELEASE_ASSERT(0, "%d", node->opcode);
}
......@@ -1812,22 +1823,18 @@ private:
assert(state != PARTIAL);
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* iffalse = entry_blocks[node->iffalse];
nonzero->decvref(emitter);
endBlock(FINISHED);
emitter.getBuilder()->CreateCondBr(llvm_nonzero, iftrue, iffalse);
emitter.getBuilder()->CreateCondBr(v, iftrue, iffalse);
}
void doExpr(AST_Expr* node, UnwindInfo unw_info) {
......
......@@ -24,6 +24,10 @@
namespace pyston {
#ifndef NDEBUG
int AST::next_lineno = 100000;
#endif
llvm::StringRef getOpSymbol(int op_type) {
switch (op_type) {
case AST_TYPE::Add:
......@@ -1448,6 +1452,9 @@ bool PrintVisitor::visit_langprimitive(AST_LangPrimitive* node) {
case AST_LangPrimitive::NONE:
printf("NONE");
break;
case AST_LangPrimitive::NONZERO:
printf("NONZERO");
break;
default:
RELEASE_ASSERT(0, "%d", node->opcode);
}
......
......@@ -148,7 +148,17 @@ public:
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) {}
#endif
};
class AST_expr : public AST {
......@@ -966,6 +976,7 @@ public:
IMPORT_NAME,
IMPORT_STAR,
NONE,
NONZERO,
} opcode;
std::vector<AST_expr*> args;
......
......@@ -131,6 +131,22 @@ private:
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* key = remapExpr(node->key);
AST_expr* value = remapExpr(node->value);
......@@ -179,7 +195,7 @@ private:
push_back(j);
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();
body_block->info = "comprehension_body";
......@@ -204,7 +220,7 @@ private:
pushAssign(c->target, makeName(next_name, AST_TYPE::Load));
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();
br->test = remapped;
push_back(br);
......@@ -284,7 +300,7 @@ private:
AST_Branch* makeBranch(AST_expr* test) {
AST_Branch* rtn = new AST_Branch();
rtn->test = test;
rtn->test = callNonzero(test);
rtn->col_offset = test->col_offset;
rtn->lineno = test->lineno;
return rtn;
......@@ -520,7 +536,7 @@ private:
pushAssign(name, val);
AST_Branch* br = new AST_Branch();
br->test = _dup(val);
br->test = callNonzero(_dup(val));
push_back(br);
CFGBlock* was_block = curblock;
......@@ -637,7 +653,7 @@ private:
pushAssign(name, val);
AST_Branch* br = new AST_Branch();
br->test = makeName(name, AST_TYPE::Load);
br->test = callNonzero(makeName(name, AST_TYPE::Load));
push_back(br);
CFGBlock* was_block = curblock;
......@@ -725,6 +741,9 @@ private:
for (AST_expr* if_condition : c->ifs) {
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;
insert_point->push_back(if_block);
......@@ -751,13 +770,14 @@ private:
AST_expr* remapIfExp(AST_IfExp* node) {
std::string rtn_name = nodeName(node);
CFGBlock* starting_block = curblock;
AST_Branch* br = new AST_Branch();
br->col_offset = node->col_offset;
br->lineno = node->lineno;
br->test = remapExpr(node->test);
br->test = callNonzero(remapExpr(node->test));
push_back(br);
CFGBlock* starting_block = curblock;
CFGBlock* iftrue = cfg->addBlock();
iftrue->info = "iftrue";
br->iftrue = iftrue;
......@@ -1055,6 +1075,10 @@ public:
#endif
curblock->push_back(node);
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:
bool visit_assert(AST_Assert* node) override {
AST_Branch* br = new AST_Branch();
br->test = remapExpr(node->test);
br->test = callNonzero(remapExpr(node->test));
push_back(br);
CFGBlock* iffalse = cfg->addBlock();
......@@ -1498,7 +1522,7 @@ public:
AST_Branch* br = new AST_Branch();
br->col_offset = node->col_offset;
br->lineno = node->lineno;
br->test = remapExpr(node->test);
br->test = callNonzero(remapExpr(node->test));
push_back(br);
CFGBlock* starting_block = curblock;
......@@ -1827,7 +1851,7 @@ public:
is_caught_here->args.push_back(makeNum(1)); // flag: false_on_noncls
AST_Branch* br = new AST_Branch();
br->test = remapExpr(is_caught_here);
br->test = callNonzero(remapExpr(is_caught_here));
CFGBlock* exc_handle = cfg->addBlock();
exc_next = cfg->addDeferredBlock();
......@@ -2153,8 +2177,18 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
for (auto b : rtn->blocks)
flatten(b->body, flattened, true);
std::unordered_set<AST*> deduped(flattened.begin(), flattened.end());
assert(deduped.size() == flattened.size());
std::unordered_map<AST*, int> deduped;
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
#endif
......
......@@ -1631,6 +1631,8 @@ bool isUserDefined(BoxedClass* cls) {
}
extern "C" bool nonzero(Box* obj) {
assert(gc::isValidGCObject(obj));
static StatCounter slowpath_nonzero("slowpath_nonzero");
std::unique_ptr<Rewriter> rewriter(
......@@ -1689,6 +1691,8 @@ extern "C" bool nonzero(Box* obj) {
// go through descriptor logic
Box* func = getclsattr_internal(obj, "__nonzero__", NULL);
if (!func)
func = getclsattr_internal(obj, "__len__", NULL);
if (func == NULL) {
ASSERT(isUserDefined(obj->cls) || obj->cls == classobj_cls, "%s.__nonzero__",
......@@ -1697,6 +1701,7 @@ extern "C" bool nonzero(Box* obj) {
}
Box* r = runtimeCall0(func, ArgPassSpec(0));
// I believe this behavior is handled by the slot wrappers in CPython:
if (r->cls == bool_cls) {
BoxedBool* b = static_cast<BoxedBool*>(r);
bool rtn = b->n;
......
......@@ -34,4 +34,16 @@ if 0:
print bool(c) # this will fail
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 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 @@
# Exception-catching is supposed to go through __subclasscheck__
class MyException(Exception):
pass
class M(type):
def __instancecheck__(self, instance):
print "instancecheck", instance
......@@ -10,10 +13,19 @@ class M(type):
def __subclasscheck__(self, sub):
print "subclasscheck", sub
if self.throw_on_subclasscheck:
raise MyException()
return True
class E(Exception):
__metaclass__ = M
throw_on_subclasscheck = False
class F(Exception):
__metaclass__ = M
throw_on_subclasscheck = True
print 1
print isinstance(E(), E) # does not print anything due to special-casing
......@@ -35,3 +47,12 @@ except E:
pass
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