Commit 3b485047 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Create classdef scopes when evaluating classes

Not completely correct/working yet, but finally start
doing classes the right way.
parent d8bdaa0c
...@@ -41,6 +41,8 @@ public: ...@@ -41,6 +41,8 @@ public:
} }
virtual bool refersToClosure(const std::string name) { return false; } virtual bool refersToClosure(const std::string name) { return false; }
virtual bool saveInClosure(const std::string name) { return false; } virtual bool saveInClosure(const std::string name) { return false; }
virtual const std::unordered_set<std::string>& getClassDefLocalNames() { RELEASE_ASSERT(0, ""); }
}; };
struct ScopingAnalysis::ScopeNameUsage { struct ScopingAnalysis::ScopeNameUsage {
...@@ -58,7 +60,12 @@ struct ScopingAnalysis::ScopeNameUsage { ...@@ -58,7 +60,12 @@ struct ScopingAnalysis::ScopeNameUsage {
StrSet referenced_from_nested; StrSet referenced_from_nested;
StrSet got_from_closure; StrSet got_from_closure;
ScopeNameUsage(AST* node, ScopeNameUsage* parent) : node(node), parent(parent) {} ScopeNameUsage(AST* node, ScopeNameUsage* parent) : node(node), parent(parent) {
if (node->type == AST_TYPE::ClassDef) {
// classes have an implicit write to "__module__"
written.insert("__module__");
}
}
}; };
class ScopeInfoBase : public ScopeInfo { class ScopeInfoBase : public ScopeInfo {
...@@ -101,6 +108,11 @@ public: ...@@ -101,6 +108,11 @@ public:
return false; return false;
return usage->referenced_from_nested.count(name) != 0; return usage->referenced_from_nested.count(name) != 0;
} }
virtual const std::unordered_set<std::string>& getClassDefLocalNames() {
RELEASE_ASSERT(usage->node->type == AST_TYPE::ClassDef, "");
return usage->written;
}
}; };
class NameCollectorVisitor : public ASTVisitor { class NameCollectorVisitor : public ASTVisitor {
......
...@@ -33,6 +33,11 @@ public: ...@@ -33,6 +33,11 @@ public:
virtual bool refersToGlobal(const std::string& name) = 0; virtual bool refersToGlobal(const std::string& name) = 0;
virtual bool refersToClosure(const std::string name) = 0; virtual bool refersToClosure(const std::string name) = 0;
virtual bool saveInClosure(const std::string name) = 0; virtual bool saveInClosure(const std::string name) = 0;
// Get the names set within a classdef that should be forwarded on to
// the metaclass constructor.
// An error to call this on a non-classdef node.
virtual const std::unordered_set<std::string>& getClassDefLocalNames() = 0;
}; };
class ScopingAnalysis { class ScopingAnalysis {
......
...@@ -459,7 +459,9 @@ private: ...@@ -459,7 +459,9 @@ private:
} }
virtual void visit_classdef(AST_ClassDef* node) { virtual void visit_classdef(AST_ClassDef* node) {
CompilerType* t = typeFromClass(type_cls); // TODO should we speculate that classdefs will generally return a class?
// CompilerType* t = typeFromClass(type_cls);
CompilerType* t = UNKNOWN;
_doSet(node->name, t); _doSet(node->name, t);
} }
......
...@@ -43,7 +43,7 @@ namespace pyston { ...@@ -43,7 +43,7 @@ namespace pyston {
// TODO terrible place for these! // TODO terrible place for these!
SourceInfo::ArgNames::ArgNames(AST* ast) { SourceInfo::ArgNames::ArgNames(AST* ast) {
if (ast->type == AST_TYPE::Module) { if (ast->type == AST_TYPE::Module || ast->type == AST_TYPE::ClassDef) {
args = NULL; args = NULL;
kwarg = vararg = NULL; kwarg = vararg = NULL;
} else if (ast->type == AST_TYPE::FunctionDef) { } else if (ast->type == AST_TYPE::FunctionDef) {
...@@ -59,6 +59,8 @@ SourceInfo::ArgNames::ArgNames(AST* ast) { ...@@ -59,6 +59,8 @@ SourceInfo::ArgNames::ArgNames(AST* ast) {
const std::string SourceInfo::getName() { const std::string SourceInfo::getName() {
assert(ast); assert(ast);
switch (ast->type) { switch (ast->type) {
case AST_TYPE::ClassDef:
return ast_cast<AST_ClassDef>(ast)->name;
case AST_TYPE::FunctionDef: case AST_TYPE::FunctionDef:
return ast_cast<AST_FunctionDef>(ast)->name; return ast_cast<AST_FunctionDef>(ast)->name;
case AST_TYPE::Module: case AST_TYPE::Module:
...@@ -71,6 +73,8 @@ const std::string SourceInfo::getName() { ...@@ -71,6 +73,8 @@ const std::string SourceInfo::getName() {
const std::vector<AST_stmt*>& SourceInfo::getBody() { const std::vector<AST_stmt*>& SourceInfo::getBody() {
assert(ast); assert(ast);
switch (ast->type) { switch (ast->type) {
case AST_TYPE::ClassDef:
return ast_cast<AST_ClassDef>(ast)->body;
case AST_TYPE::FunctionDef: case AST_TYPE::FunctionDef:
return ast_cast<AST_FunctionDef>(ast)->body; return ast_cast<AST_FunctionDef>(ast)->body;
case AST_TYPE::Module: case AST_TYPE::Module:
...@@ -165,7 +169,7 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E ...@@ -165,7 +169,7 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
// Do the analysis now if we had deferred it earlier: // Do the analysis now if we had deferred it earlier:
if (source->cfg == NULL) { if (source->cfg == NULL) {
assert(source->ast); assert(source->ast);
source->cfg = computeCFG(source->ast->type, source->getBody()); source->cfg = computeCFG(source, source->getBody());
source->liveness = computeLivenessInfo(source->cfg); source->liveness = computeLivenessInfo(source->cfg);
source->phis = computeRequiredPhis(source->arg_names, source->cfg, source->liveness, source->phis = computeRequiredPhis(source->arg_names, source->cfg, source->liveness,
source->scoping->getScopeInfoForNode(source->ast)); source->scoping->getScopeInfoForNode(source->ast));
...@@ -228,7 +232,7 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) { ...@@ -228,7 +232,7 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
ScopingAnalysis* scoping = runScopingAnalysis(m); ScopingAnalysis* scoping = runScopingAnalysis(m);
SourceInfo* si = new SourceInfo(bm, scoping, m); SourceInfo* si = new SourceInfo(bm, scoping, m);
si->cfg = computeCFG(AST_TYPE::Module, m->body); si->cfg = computeCFG(si, m->body);
si->liveness = computeLivenessInfo(si->cfg); si->liveness = computeLivenessInfo(si->cfg);
si->phis = computeRequiredPhis(si->arg_names, si->cfg, si->liveness, si->scoping->getScopeInfoForNode(si->ast)); si->phis = computeRequiredPhis(si->arg_names, si->cfg, si->liveness, si->scoping->getScopeInfoForNode(si->ast));
......
...@@ -791,23 +791,14 @@ private: ...@@ -791,23 +791,14 @@ private:
return v; return v;
} }
CompilerVariable* evalName(AST_Name* node, ExcInfo exc_info) { ConcreteCompilerVariable* _getGlobal(AST_Name* node, ExcInfo exc_info) {
assert(state != PARTIAL);
auto scope_info = irstate->getScopeInfo();
if (scope_info->refersToGlobal(node->id)) {
if (1) {
// Method 1: calls into the runtime getGlobal(), which handles things like falling back to builtins
// or raising the correct error message.
bool do_patchpoint = ENABLE_ICGETGLOBALS && (irstate->getEffortLevel() != EffortLevel::INTERPRETED); bool do_patchpoint = ENABLE_ICGETGLOBALS && (irstate->getEffortLevel() != EffortLevel::INTERPRETED);
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());
std::vector<llvm::Value*> llvm_args; std::vector<llvm::Value*> llvm_args;
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::Value* uncasted llvm::Value* uncasted
...@@ -816,21 +807,20 @@ private: ...@@ -816,21 +807,20 @@ private:
return new ConcreteCompilerVariable(UNKNOWN, r, true); return new ConcreteCompilerVariable(UNKNOWN, r, true);
} else { } else {
llvm::Value* r llvm::Value* r
= emitter.createCall2( = emitter.createCall2(exc_info, g.funcs.getGlobal,
exc_info, g.funcs.getGlobal,
embedConstantPtr(irstate->getSourceInfo()->parent_module, g.llvm_module_type_ptr), embedConstantPtr(irstate->getSourceInfo()->parent_module, g.llvm_module_type_ptr),
embedConstantPtr(&node->id, g.llvm_str_type_ptr)).getInstruction(); embedConstantPtr(&node->id, g.llvm_str_type_ptr)).getInstruction();
return new ConcreteCompilerVariable(UNKNOWN, r, true); return new ConcreteCompilerVariable(UNKNOWN, r, true);
} }
} else {
// Method 2 [testing-only]: (ab)uses existing getattr patchpoints and just calls module.getattr()
// This option exists for performance testing because method 1 does not currently use patchpoints.
ConcreteCompilerVariable* mod = new ConcreteCompilerVariable(
MODULE, embedConstantPtr(irstate->getSourceInfo()->parent_module, g.llvm_value_type_ptr), false);
CompilerVariable* attr = mod->getattr(emitter, getOpInfoForNode(node, exc_info), &node->id, false);
mod->decvref(emitter);
return attr;
} }
CompilerVariable* evalName(AST_Name* node, ExcInfo exc_info) {
assert(state != PARTIAL);
auto scope_info = irstate->getScopeInfo();
if (scope_info->refersToGlobal(node->id)) {
return _getGlobal(node, exc_info);
} else if (scope_info->refersToClosure(node->id)) { } else if (scope_info->refersToClosure(node->id)) {
assert(scope_info->takesClosure()); assert(scope_info->takesClosure());
...@@ -840,6 +830,11 @@ private: ...@@ -840,6 +830,11 @@ private:
return closure->getattr(emitter, getEmptyOpInfo(exc_info), &node->id, false); return closure->getattr(emitter, getEmptyOpInfo(exc_info), &node->id, false);
} else { } else {
if (symbol_table.find(node->id) == symbol_table.end()) { if (symbol_table.find(node->id) == symbol_table.end()) {
// classdefs have different scoping rules than functions:
if (irstate->getSourceInfo()->ast->type == AST_TYPE::ClassDef) {
return _getGlobal(node, exc_info);
}
// TODO should mark as DEAD here, though we won't end up setting all the names appropriately // TODO should mark as DEAD here, though we won't end up setting all the names appropriately
// state = DEAD; // state = DEAD;
llvm::CallSite call = emitter.createCall2(exc_info, g.funcs.assertNameDefined, getConstantInt(0, g.i1), llvm::CallSite call = emitter.createCall2(exc_info, g.funcs.assertNameDefined, getConstantInt(0, g.i1),
...@@ -850,7 +845,36 @@ private: ...@@ -850,7 +845,36 @@ private:
std::string defined_name = _getFakeName("is_defined", node->id.c_str()); std::string defined_name = _getFakeName("is_defined", node->id.c_str());
ConcreteCompilerVariable* is_defined = static_cast<ConcreteCompilerVariable*>(_popFake(defined_name, true)); ConcreteCompilerVariable* is_defined = static_cast<ConcreteCompilerVariable*>(_popFake(defined_name, true));
if (is_defined) { if (is_defined) {
// classdefs have different scoping rules than functions:
if (irstate->getSourceInfo()->ast->type == AST_TYPE::ClassDef) {
llvm::BasicBlock* from_local
= llvm::BasicBlock::Create(g.context, "from_local", irstate->getLLVMFunction());
llvm::BasicBlock* from_global
= llvm::BasicBlock::Create(g.context, "from_global", irstate->getLLVMFunction());
llvm::BasicBlock* join = llvm::BasicBlock::Create(g.context, "join", irstate->getLLVMFunction());
emitter.getBuilder()->CreateCondBr(is_defined->getValue(), from_local, from_global);
emitter.getBuilder()->SetInsertPoint(from_local);
CompilerVariable* local = symbol_table[node->id];
ConcreteCompilerVariable* converted_local = local->makeConverted(emitter, local->getBoxType());
// don't decvref local here, because are manufacturing a new vref
emitter.getBuilder()->CreateBr(join);
emitter.getBuilder()->SetInsertPoint(from_global);
ConcreteCompilerVariable* global = _getGlobal(node, exc_info);
emitter.getBuilder()->CreateBr(join);
emitter.getBuilder()->SetInsertPoint(join);
llvm::PHINode* phi = emitter.getBuilder()->CreatePHI(g.llvm_value_type_ptr, 2, node->id);
phi->addIncoming(converted_local->getValue(), from_local);
phi->addIncoming(global->getValue(), from_global);
return new ConcreteCompilerVariable(UNKNOWN, phi, true);
}
emitter.createCall2(exc_info, g.funcs.assertNameDefined, is_defined->getValue(), emitter.createCall2(exc_info, g.funcs.assertNameDefined, is_defined->getValue(),
getStringConstantPtr(node->id + '\0')); getStringConstantPtr(node->id + '\0'));
} }
...@@ -1385,85 +1409,44 @@ private: ...@@ -1385,85 +1409,44 @@ private:
assert(node->type == AST_TYPE::ClassDef); assert(node->type == AST_TYPE::ClassDef);
ScopeInfo* scope_info = irstate->getSourceInfo()->scoping->getScopeInfoForNode(node); ScopeInfo* scope_info = irstate->getSourceInfo()->scoping->getScopeInfoForNode(node);
assert(scope_info); assert(scope_info);
assert(!scope_info->takesClosure());
RELEASE_ASSERT(node->bases.size() == 1, ""); RELEASE_ASSERT(node->bases.size() == 1, "");
RELEASE_ASSERT(node->decorator_list.size() == 0, "");
CompilerVariable* base = evalExpr(node->bases[0], exc_info); CompilerVariable* base = evalExpr(node->bases[0], exc_info);
ConcreteCompilerVariable* converted_base = base->makeConverted(emitter, base->getBoxType()); ConcreteCompilerVariable* converted_base = base->makeConverted(emitter, base->getBoxType());
base->decvref(emitter); base->decvref(emitter);
llvm::Value* classobj CLFunction* cl = _wrapClassDef(node);
= emitter.createCall3(exc_info, g.funcs.createUserClass, embedConstantPtr(&node->name, g.llvm_str_type_ptr),
converted_base->getValue(),
embedConstantPtr(irstate->getSourceInfo()->parent_module, g.llvm_module_type_ptr))
.getInstruction();
ConcreteCompilerVariable* cls = new ConcreteCompilerVariable(typeFromClass(type_cls), classobj, true);
// CompilerVariable* name = makeStr(&node->name);
// cls->setattr(emitter, "__name__", name);
// name->decvref(emitter);
bool had_docstring = false; // TODO duplication with doFunctionDef:
static std::string doc_str("__doc__"); CompilerVariable* created_closure = NULL;
if (scope_info->takesClosure()) {
created_closure = _getFake(CREATED_CLOSURE_NAME, false);
assert(created_closure);
}
// TODO: rather than special-casing all the different types and implementing a small irgenerator, // TODO kind of silly to create the function just to usually-delete it afterwards;
// do the full thing and run through an irgenerator for real. // one reason to do this is to pass the closure through if necessary,
for (int i = 0, n = node->body.size(); i < n; i++) { // but since the classdef can't create its own closure, shouldn't need to explicitly
AST_TYPE::AST_TYPE type = node->body[i]->type; // create that scope to pass the closure through.
if (type == AST_TYPE::Pass) { CompilerVariable* func = makeFunction(emitter, cl, created_closure, {});
continue;
} else if (type == AST_TYPE::Expr) {
AST_Expr* expr = ast_cast<AST_Expr>(node->body[i]);
if (expr->value->type == AST_TYPE::Str) { CompilerVariable* attr_dict = func->call(emitter, getEmptyOpInfo(exc_info), ArgPassSpec(0), {}, NULL);
if (i == 0) {
had_docstring = true;
CompilerVariable* str = evalExpr(expr->value, exc_info);
cls->setattr(emitter, getEmptyOpInfo(exc_info), &doc_str, str);
}
} else {
fprintf(stderr, "Currently don't support non-noop expressions in classes:\n");
printf("On line %d: ", expr->lineno);
print_ast(expr);
printf("\n");
abort();
}
} else if (type == AST_TYPE::FunctionDef) {
AST_FunctionDef* fdef = ast_cast<AST_FunctionDef>(node->body[i]);
assert(fdef->args->defaults.size() == 0);
assert(fdef->decorator_list.size() == 0);
ScopeInfo* scope_info = irstate->getSourceInfo()->scoping->getScopeInfoForNode(fdef);
CLFunction* cl = this->_wrapFunction(fdef);
assert(!scope_info->takesClosure());
CompilerVariable* func = makeFunction(emitter, cl, NULL, {});
cls->setattr(emitter, getEmptyOpInfo(exc_info), &fdef->name, func);
func->decvref(emitter); func->decvref(emitter);
} else if (type == AST_TYPE::Assign) {
AST_Assign* asgn = ast_cast<AST_Assign>(node->body[i]);
RELEASE_ASSERT(asgn->targets.size() == 1, "");
RELEASE_ASSERT(asgn->targets[0]->type == AST_TYPE::Name, ""); ConcreteCompilerVariable* converted_attr_dict = attr_dict->makeConverted(emitter, attr_dict->getBoxType());
AST_Name* target = ast_cast<AST_Name>(asgn->targets[0]); attr_dict->decvref(emitter);
RELEASE_ASSERT(target->id != "__metaclass__", "");
RELEASE_ASSERT(asgn->value->type == AST_TYPE::Num || asgn->value->type == AST_TYPE::Str, "%d",
asgn->lineno);
CompilerVariable* val = evalExpr(asgn->value, exc_info);
cls->setattr(emitter, getEmptyOpInfo(exc_info), &target->id, val);
} else if (type == AST_TYPE::Pass) {
// do nothing
} else {
RELEASE_ASSERT(0, "%d", type);
}
}
if (!had_docstring) { llvm::Value* classobj
cls->setattr(emitter, getEmptyOpInfo(exc_info), &doc_str, getNone()); = emitter.createCall3(exc_info, g.funcs.createUserClass, embedConstantPtr(&node->name, g.llvm_str_type_ptr),
} converted_base->getValue(), converted_attr_dict->getValue()).getInstruction();
// Note: createuserClass is free to manufacture non-class objects
auto cls = new ConcreteCompilerVariable(UNKNOWN, classobj, true);
_doSet(node->name, cls, exc_info); _doSet(node->name, cls, exc_info);
cls->decvref(emitter); cls->decvref(emitter);
} }
...@@ -1528,6 +1511,20 @@ private: ...@@ -1528,6 +1511,20 @@ private:
return cl; return cl;
} }
CLFunction* _wrapClassDef(AST_ClassDef* node) {
// TODO duplication with _wrapFunction
static std::unordered_map<AST_ClassDef*, CLFunction*> made;
CLFunction*& cl = made[node];
if (cl == NULL) {
SourceInfo* si
= new SourceInfo(irstate->getSourceInfo()->parent_module, irstate->getSourceInfo()->scoping, node);
si->ast = node;
cl = new CLFunction(0, 0, 0, 0, si);
}
return cl;
}
void doFunctionDef(AST_FunctionDef* node, ExcInfo exc_info) { void doFunctionDef(AST_FunctionDef* node, ExcInfo exc_info) {
if (state == PARTIAL) if (state == PARTIAL)
return; return;
......
...@@ -688,6 +688,8 @@ public: ...@@ -688,6 +688,8 @@ public:
virtual void* accept_expr(ExprVisitor* v); virtual void* accept_expr(ExprVisitor* v);
AST_Str() : AST_expr(AST_TYPE::Str) {} AST_Str() : AST_expr(AST_TYPE::Str) {}
AST_Str(const std::string& s) : AST_expr(AST_TYPE::Str), s(s) {}
AST_Str(const std::string&& s) : AST_expr(AST_TYPE::Str), s(std::move(s)) {}
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Str; static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Str;
}; };
......
...@@ -19,8 +19,11 @@ ...@@ -19,8 +19,11 @@
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include "analysis/scoping_analysis.h"
#include "core/ast.h" #include "core/ast.h"
#include "core/options.h" #include "core/options.h"
#include "core/types.h"
#include "runtime/types.h"
//#undef VERBOSITY //#undef VERBOSITY
//#define VERBOSITY(x) 2 //#define VERBOSITY(x) 2
...@@ -49,6 +52,15 @@ void CFGBlock::unconnectFrom(CFGBlock* successor) { ...@@ -49,6 +52,15 @@ void CFGBlock::unconnectFrom(CFGBlock* successor) {
successor->predecessors.end()); successor->predecessors.end());
} }
static AST_Name* makeName(const std::string& id, AST_TYPE::AST_TYPE ctx_type, int lineno = 0, int col_offset = 0) {
AST_Name* name = new AST_Name();
name->id = id;
name->col_offset = col_offset;
name->lineno = lineno;
name->ctx_type = ctx_type;
return name;
}
class CFGVisitor : public ASTVisitor { class CFGVisitor : public ASTVisitor {
private: private:
AST_TYPE::AST_TYPE root_type; AST_TYPE::AST_TYPE root_type;
...@@ -327,15 +339,6 @@ private: ...@@ -327,15 +339,6 @@ private:
return call; return call;
} }
AST_Name* makeName(const std::string& id, AST_TYPE::AST_TYPE ctx_type, int lineno = 0, int col_offset = 0) {
AST_Name* name = new AST_Name();
name->id = id;
name->col_offset = col_offset;
name->lineno = lineno;
name->ctx_type = ctx_type;
return name;
}
AST_stmt* makeAssign(AST_expr* target, AST_expr* val) { AST_stmt* makeAssign(AST_expr* target, AST_expr* val) {
AST_Assign* assign = new AST_Assign(); AST_Assign* assign = new AST_Assign();
assign->targets.push_back(target); assign->targets.push_back(target);
...@@ -1636,13 +1639,63 @@ void CFG::print() { ...@@ -1636,13 +1639,63 @@ void CFG::print() {
delete pv; delete pv;
} }
CFG* computeCFG(AST_TYPE::AST_TYPE root_type, std::vector<AST_stmt*> body) { CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
CFG* rtn = new CFG(); CFG* rtn = new CFG();
CFGVisitor visitor(root_type, rtn); CFGVisitor visitor(source->ast->type, rtn);
// In a classdef, the "__module__" attribute is immediately available:
if (source->ast->type == AST_TYPE::ClassDef) {
Box* module_name = source->parent_module->getattr("__name__", NULL, NULL);
assert(module_name->cls == str_cls);
AST_Assign* module_assign = new AST_Assign();
module_assign->targets.push_back(makeName("__module__", AST_TYPE::Store));
module_assign->value = new AST_Str(static_cast<BoxedString*>(module_name)->s);
visitor.push_back(module_assign);
}
for (int i = 0; i < body.size(); i++) { for (int i = 0; i < body.size(); i++) {
body[i]->accept(&visitor); body[i]->accept(&visitor);
} }
// The functions we create for classdefs are supposed to return a dictionary of their locals.
// This is the place that we add all of that:
if (source->ast->type == AST_TYPE::ClassDef) {
ScopeInfo* scope_info = source->scoping->getScopeInfoForNode(source->ast);
auto written_names = scope_info->getClassDefLocalNames();
AST_Dict* rtn_dict = new AST_Dict();
// It'd be ok to add __doc__ to the dict multiple times, since the last one would win
if (written_names.count("__doc__") == 0) {
bool found_docstr = false;
if (body.size() && body[0]->type == AST_TYPE::Expr) {
AST_Expr* first_expr = ast_cast<AST_Expr>(body[0]);
if (first_expr->value->type == AST_TYPE::Str) {
found_docstr = true;
rtn_dict->keys.push_back(new AST_Str("__doc__"));
rtn_dict->values.push_back(first_expr->value);
}
}
if (!found_docstr) {
rtn_dict->keys.push_back(new AST_Str("__doc__"));
rtn_dict->values.push_back(makeName("None", AST_TYPE::Load));
}
}
// Even if the user never explicitly wrote to __module__, there was an
// implicit write:
assert(written_names.count("__module__"));
for (auto s : written_names) {
rtn_dict->keys.push_back(new AST_Str(s));
rtn_dict->values.push_back(makeName(s, AST_TYPE::Load));
}
AST_Return* rtn = new AST_Return();
rtn->value = rtn_dict;
visitor.push_back(rtn);
} else {
// Put a fake "return" statement at the end of every function just to make sure they all have one; // Put a fake "return" statement at the end of every function just to make sure they all have one;
// we already have to support multiple return statements in a function, but this way we can avoid // we already have to support multiple return statements in a function, but this way we can avoid
// having to support not having a return statement: // having to support not having a return statement:
...@@ -1650,6 +1703,7 @@ CFG* computeCFG(AST_TYPE::AST_TYPE root_type, std::vector<AST_stmt*> body) { ...@@ -1650,6 +1703,7 @@ CFG* computeCFG(AST_TYPE::AST_TYPE root_type, std::vector<AST_stmt*> body) {
return_stmt->lineno = return_stmt->col_offset = 0; return_stmt->lineno = return_stmt->col_offset = 0;
return_stmt->value = NULL; return_stmt->value = NULL;
visitor.push_back(return_stmt); visitor.push_back(return_stmt);
}
if (VERBOSITY("cfg") >= 2) { if (VERBOSITY("cfg") >= 2) {
printf("Before cfg checking and transformations:\n"); printf("Before cfg checking and transformations:\n");
......
...@@ -93,7 +93,8 @@ public: ...@@ -93,7 +93,8 @@ public:
void print(); void print();
}; };
CFG* computeCFG(AST_TYPE::AST_TYPE root_type, std::vector<AST_stmt*> body); class SourceInfo;
CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body);
} }
#endif #endif
...@@ -291,11 +291,14 @@ const AllocationKind conservative_kind(&conservativeGCHandler, NULL); ...@@ -291,11 +291,14 @@ const AllocationKind conservative_kind(&conservativeGCHandler, NULL);
BoxedTuple* EmptyTuple; BoxedTuple* EmptyTuple;
} }
extern "C" Box* createUserClass(std::string* name, Box* _base, BoxedModule* parent_module) { extern "C" Box* createUserClass(std::string* name, Box* _base, Box* _attr_dict) {
assert(_base); assert(_base);
assert(isSubclass(_base->cls, type_cls)); assert(isSubclass(_base->cls, type_cls));
BoxedClass* base = static_cast<BoxedClass*>(_base); BoxedClass* base = static_cast<BoxedClass*>(_base);
ASSERT(_attr_dict->cls == dict_cls, "%s", getTypeName(_attr_dict)->c_str());
BoxedDict* attr_dict = static_cast<BoxedDict*>(_attr_dict);
BoxedClass* made; BoxedClass* made;
if (base->instancesHaveAttrs()) { if (base->instancesHaveAttrs()) {
...@@ -305,10 +308,14 @@ extern "C" Box* createUserClass(std::string* name, Box* _base, BoxedModule* pare ...@@ -305,10 +308,14 @@ extern "C" Box* createUserClass(std::string* name, Box* _base, BoxedModule* pare
made = new BoxedClass(base, base->instance_size, base->instance_size + sizeof(HCAttrs), true); made = new BoxedClass(base, base->instance_size, base->instance_size + sizeof(HCAttrs), true);
} }
made->giveAttr("__name__", boxString(*name)); for (const auto& p : attr_dict->d) {
assert(p.first->cls == str_cls);
made->giveAttr(static_cast<BoxedString*>(p.first)->s, p.second);
}
// Note: make sure to do this after assigning the attrs, since it will overwrite any defined __name__
made->setattr("__name__", boxString(*name), NULL);
Box* modname = parent_module->getattr("__name__", NULL, NULL);
made->giveAttr("__module__", modname);
return made; return made;
} }
...@@ -604,6 +611,8 @@ BoxedModule* createModule(const std::string& name, const std::string& fn) { ...@@ -604,6 +611,8 @@ BoxedModule* createModule(const std::string& name, const std::string& fn) {
Box* b_name = boxStringPtr(&name); Box* b_name = boxStringPtr(&name);
assert(d->d.count(b_name) == 0); assert(d->d.count(b_name) == 0);
d->d[b_name] = module; d->d[b_name] = module;
module->giveAttr("__doc__", None);
return module; return module;
} }
......
...@@ -90,7 +90,7 @@ extern "C" void listAppendInternal(Box* self, Box* v); ...@@ -90,7 +90,7 @@ extern "C" void listAppendInternal(Box* self, Box* v);
extern "C" void listAppendArrayInternal(Box* self, Box** v, int nelts); extern "C" void listAppendArrayInternal(Box* self, Box** v, int nelts);
extern "C" Box* boxCLFunction(CLFunction* f, BoxedClosure* closure, std::initializer_list<Box*> defaults); extern "C" Box* boxCLFunction(CLFunction* f, BoxedClosure* closure, std::initializer_list<Box*> defaults);
extern "C" CLFunction* unboxCLFunction(Box* b); extern "C" CLFunction* unboxCLFunction(Box* b);
extern "C" Box* createUserClass(std::string* name, Box* base, BoxedModule* parent_module); extern "C" Box* createUserClass(std::string* name, Box* base, Box* attr_dict);
extern "C" double unboxFloat(Box* b); extern "C" double unboxFloat(Box* b);
extern "C" Box* createDict(); extern "C" Box* createDict();
extern "C" Box* createList(); extern "C" Box* createList();
......
# expected: fail
# - arbitrary stuff in classes
# You can put arbitrary stuff in class definitions, which end up being added as class attributes # You can put arbitrary stuff in class definitions, which end up being added as class attributes
class C(object): class C(object):
...@@ -27,6 +24,7 @@ class C(object): ...@@ -27,6 +24,7 @@ class C(object):
pass pass
[123] [123]
print C.__module__
class D(object): class D(object):
x = 1 x = 1
......
print __doc__
__doc__ = "module_doc"
print __doc__
class C1(object): class C1(object):
print 1, __doc__
"hello world" "hello world"
print 2, __doc__
print C1.__doc__ print C1.__doc__
class C2(object): class C2(object):
...@@ -8,8 +14,21 @@ class C2(object): ...@@ -8,8 +14,21 @@ class C2(object):
print C2.__doc__ print C2.__doc__
class C3(object): class C3(object):
print __doc__
pass pass
print C3.__doc__ print "C3", C3.__doc__
class C4(object):
print 1, __doc__
"doc1"
print 2, __doc__
__doc__ = "doc2"
print 3, __doc__
print C4.__doc__
class C5(object):
__doc__ = "doc2"
print C5.__doc__
""" """
# Not supported yet: # Not supported yet:
......
# expected: fail
# - arbitrary stuff in classes
class C(object): class C(object):
return return
# expected: fail # expected: fail
# - arbitrary stuff in classes # - WIP
X = 0 X = 0
Y = 0 Y = 0
Z = 0
W = 0
def f(val, desc):
print desc
return val
def wrapper(): def wrapper():
X = 1 X = 1
Y = 1 Y = 1
class C(object): Z = 1
W = 1
print "starting classdef"
class C(f(object, "evaluating bases")):
print __doc__, __module__, __name__ # "None __main__ __main__"
print "inside classdef"
global Y global Y
X = 2 X = 2
Y = 2 Y = 2
def f(self):
if 0:
Z = 2
# class scopes have different scoping rules than function scopes (!!):
# In a function scope, if the name has a store but isn't set on this path,
# referencing it raises a NameError.
# In a class scope, the lookup resolves to the global scope.
print Z, W # "0 1".
# The defaults for a and b should resolve to the classdef definitions, and the default
# for c should resolve to the wrapper() definition
def f(self, a=X, b=Y, c=Z, d=W):
print a, b, c, W # "2 2 0 1"
# These references should skip all of the classdef directives, # These references should skip all of the classdef directives,
# and hit the definitions in the wrapper() function # and hit the definitions in the wrapper() function
print X print X, Y # "1 1"
print Y print "done with classdef"
return C return C
wrapper()().f() wrapper()().f()
print X print X # "0"
print Y # got changed in classdef for C print Y # "2" -- got changed in classdef for C
print
class C2(object):
print __name__
__name__ = 1
print __name__
print C2.__name__
print
class C3(object):
print __module__
__module__ = 1
print __module__
print C3.__module__
"""
# not supported (del)
print
class C4(object):
print __module__
del __module__
try:
print __module__ # this should throw a NameError
assert 0
except NameError:
pass
print C4.__module__
"""
class C5(object):
try:
print not_defined
except NameError:
print "threw NameError as expected"
def make_class(add_z):
class C(object):
if add_z:
Z = 1
return C
print hasattr(make_class(False), "Z")
print hasattr(make_class(True), "Z")
...@@ -14,8 +14,16 @@ ...@@ -14,8 +14,16 @@
using namespace pyston; using namespace pyston;
TEST(func_analysis, augassign) { class AnalysisTest : public ::testing::Test {
AST_Module* module = caching_parse("../test/unittests/analysis_listcomp.py"); protected:
virtual void SetUp() {
initCodegen();
}
};
TEST_F(AnalysisTest, augassign) {
const std::string fn("../test/unittests/analysis_listcomp.py");
AST_Module* module = caching_parse(fn.c_str());
assert(module); assert(module);
ScopingAnalysis *scoping = runScopingAnalysis(module); ScopingAnalysis *scoping = runScopingAnalysis(module);
...@@ -27,7 +35,9 @@ TEST(func_analysis, augassign) { ...@@ -27,7 +35,9 @@ TEST(func_analysis, augassign) {
ASSERT_FALSE(scope_info->refersToGlobal("a")); ASSERT_FALSE(scope_info->refersToGlobal("a"));
ASSERT_FALSE(scope_info->refersToGlobal("b")); ASSERT_FALSE(scope_info->refersToGlobal("b"));
CFG* cfg = computeCFG(func->type, func->body); SourceInfo* si = new SourceInfo(createModule("__main__", fn), scoping, func);
CFG* cfg = computeCFG(si, func->body);
LivenessAnalysis* liveness = computeLivenessInfo(cfg); LivenessAnalysis* liveness = computeLivenessInfo(cfg);
//cfg->print(); //cfg->print();
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "core/threading.h" #include "core/threading.h"
#include "codegen/entry.h"
namespace pyston { namespace pyston {
......
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