From de5fde0490b75f97bf1d64cf977842c40d31c22a Mon Sep 17 00:00:00 2001 From: Travis Hance Date: Mon, 23 Mar 2015 03:15:02 -0700 Subject: [PATCH] static closures --- src/analysis/scoping_analysis.cpp | 86 +++++++++++++++++++++++--- src/analysis/scoping_analysis.h | 43 +++---------- src/codegen/ast_interpreter.cpp | 23 +++++-- src/codegen/compvars.cpp | 6 +- src/codegen/irgen/irgenerator.cpp | 44 +++++++++++-- src/codegen/runtime_hooks.cpp | 1 + src/codegen/runtime_hooks.h | 2 +- src/codegen/unwinding.cpp | 27 ++++---- src/runtime/inline/link_forcer.cpp | 1 + src/runtime/objmodel.cpp | 40 +++--------- src/runtime/objmodel.h | 3 +- src/runtime/types.cpp | 15 +++-- src/runtime/types.h | 18 +++++- test/tests/static_closure_locations.py | 25 ++++++++ 14 files changed, 225 insertions(+), 109 deletions(-) create mode 100644 test/tests/static_closure_locations.py diff --git a/src/analysis/scoping_analysis.cpp b/src/analysis/scoping_analysis.cpp index 0ba633c06..afbf7b385 100644 --- a/src/analysis/scoping_analysis.cpp +++ b/src/analysis/scoping_analysis.cpp @@ -103,10 +103,14 @@ class ModuleScopeInfo : public ScopeInfo { bool usesNameLookup() override { return false; } - bool isPassedToViaClosure(InternedString name) override { return false; } - bool areLocalsFromModule() override { return true; } + DerefInfo getDerefInfo(InternedString) override { RELEASE_ASSERT(0, "This should never get called"); } + size_t getClosureOffset(InternedString) override { RELEASE_ASSERT(0, "This should never get called"); } + size_t getClosureSize() override { RELEASE_ASSERT(0, "This should never get called"); } + std::vector> v; + const std::vector>& getAllDerefVarsAndInfo() override { return v; } + InternedString mangleName(InternedString id) override { return id; } InternedString internString(llvm::StringRef s) override { abort(); } }; @@ -165,10 +169,14 @@ class EvalExprScopeInfo : public ScopeInfo { bool usesNameLookup() override { return true; } - bool isPassedToViaClosure(InternedString name) override { return false; } - bool areLocalsFromModule() override { return false; } + DerefInfo getDerefInfo(InternedString) override { RELEASE_ASSERT(0, "This should never get called"); } + size_t getClosureOffset(InternedString) override { RELEASE_ASSERT(0, "This should never get called"); } + size_t getClosureSize() override { RELEASE_ASSERT(0, "This should never get called"); } + std::vector> v; + const std::vector>& getAllDerefVarsAndInfo() override { return v; } + InternedString mangleName(InternedString id) override { return id; } InternedString internString(llvm::StringRef s) override { abort(); } }; @@ -257,11 +265,22 @@ class ScopeInfoBase : public ScopeInfo { AST* ast; bool usesNameLookup_; + llvm::DenseMap closure_offsets; + + std::vector> allDerefVarsAndInfo; + bool allDerefVarsAndInfoCached; + public: ScopeInfoBase(ScopeInfo* parent, ScopingAnalysis::ScopeNameUsage* usage, AST* ast, bool usesNameLookup) - : parent(parent), usage(usage), ast(ast), usesNameLookup_(usesNameLookup) { + : parent(parent), usage(usage), ast(ast), usesNameLookup_(usesNameLookup), allDerefVarsAndInfoCached(false) { assert(usage); assert(ast); + + int i = 0; + for (auto& p : usage->referenced_from_nested) { + closure_offsets[p] = i; + i++; + } } ~ScopeInfoBase() override { delete this->usage; } @@ -300,20 +319,67 @@ class ScopeInfoBase : public ScopeInfo { bool usesNameLookup() override { return usesNameLookup_; } - bool isPassedToViaClosure(InternedString name) override { - if (isCompilerCreatedName(name)) - return false; + bool areLocalsFromModule() override { return false; } + + DerefInfo getDerefInfo(InternedString name) override { + assert(getScopeTypeOfName(name) == VarScopeType::DEREF); + + // TODO pre-compute this? + + size_t parentCounter = 0; + // Casting to a ScopeInfoBase* is okay because only a ScopeInfoBase can have a closure. + for (ScopeInfoBase* parent = static_cast(this->parent); parent != NULL; + parent = static_cast(parent->parent)) { + if (parent->createsClosure()) { + auto it = parent->closure_offsets.find(name); + if (it != parent->closure_offsets.end()) { + return DerefInfo{.num_parents_from_passed_closure = parentCounter, .offset = it->second }; + } + parentCounter++; + } + } - return usage->got_from_closure.count(name) > 0 || usage->passthrough_accesses.count(name) > 0; + RELEASE_ASSERT(0, "Should not get here"); } - bool areLocalsFromModule() override { return false; } + size_t getClosureOffset(InternedString name) override { + assert(getScopeTypeOfName(name) == VarScopeType::CLOSURE); + return closure_offsets[name]; + } + + size_t getClosureSize() override { return closure_offsets.size(); } InternedString mangleName(const InternedString id) override { return pyston::mangleName(id, usage->private_name, usage->scoping->getInternedStrings()); } InternedString internString(llvm::StringRef s) override { return usage->scoping->getInternedStrings().get(s); } + + const std::vector>& getAllDerefVarsAndInfo() override { + if (!allDerefVarsAndInfoCached) { + allDerefVarsAndInfoCached = true; + + StrSet allDerefs = usage->got_from_closure; + for (InternedString name : usage->passthrough_accesses) { + if (allDerefs.find(name) != allDerefs.end()) { + allDerefs.insert(name); + } + } + + for (InternedString name : allDerefs) { + allDerefVarsAndInfo.push_back({ name, getDerefInfo(name) }); + } + + std::sort(allDerefVarsAndInfo.begin(), allDerefVarsAndInfo.end(), derefComparator); + } + return allDerefVarsAndInfo; + } + +private: + static bool derefComparator(const std::pair& p1, + const std::pair& p2) { + return p1.second.num_parents_from_passed_closure < p2.second.num_parents_from_passed_closure; + }; }; class NameCollectorVisitor : public ASTVisitor { diff --git a/src/analysis/scoping_analysis.h b/src/analysis/scoping_analysis.h index 0ba33a809..5307582e7 100644 --- a/src/analysis/scoping_analysis.h +++ b/src/analysis/scoping_analysis.h @@ -25,6 +25,11 @@ class AST_Module; class AST_Expression; class AST_Suite; +struct DerefInfo { + size_t num_parents_from_passed_closure; + size_t offset; +}; + class ScopeInfo { public: ScopeInfo() {} @@ -75,39 +80,6 @@ class ScopeInfo { }; virtual VarScopeType getScopeTypeOfName(InternedString name) = 0; - // Returns true if the variable should be passed via a closure to this scope. - // Note that: - // (a) This can be false even if there is an entry in the closure object - // passed to the scope, if the variable is not actually used in this - // scope or any child scopes. This can happen, because the variable - // could be in the closure to be accessed by a different function, e.g. - // - // def f(); - // a = 0 - // b = 0 - // def g(): - // print a - // def h(): - // print b - // # locals() should not contain `a` even though `h` is - // # passed a closure object with `a` in it - // print locals() - // - // (b) This can be true even if it is not used in this scope, if it - // is used in a child scope. For example: - // - // def f(): - // a = 0 - // def g(): - // def h(): - // print a - // print locals() # should contain `a` - // - // This is useful because it determines whether a variable from a closure - // into the locals() dictionary. - - virtual bool isPassedToViaClosure(InternedString name) = 0; - // Returns true if the scope may contain NAME variables. // In particular, it returns true for ClassDef scope, for any scope // with an `exec` statement or `import *` statement in it, or for any @@ -116,6 +88,11 @@ class ScopeInfo { virtual bool areLocalsFromModule() = 0; + virtual DerefInfo getDerefInfo(InternedString name) = 0; + virtual const std::vector>& getAllDerefVarsAndInfo() = 0; + virtual size_t getClosureOffset(InternedString name) = 0; + virtual size_t getClosureSize() = 0; + virtual InternedString mangleName(InternedString id) = 0; virtual InternedString internString(llvm::StringRef) = 0; }; diff --git a/src/codegen/ast_interpreter.cpp b/src/codegen/ast_interpreter.cpp index fba67488f..a52d1d280 100644 --- a/src/codegen/ast_interpreter.cpp +++ b/src/codegen/ast_interpreter.cpp @@ -228,7 +228,7 @@ void ASTInterpreter::initArguments(int nargs, BoxedClosure* _closure, BoxedGener generator = _generator; if (scope_info->createsClosure()) - created_closure = createClosure(passed_closure); + created_closure = createClosure(passed_closure, scope_info->getClosureSize()); std::vector> argsArray{ arg1, arg2, arg3 }; for (int i = 3; i < nargs; ++i) @@ -354,8 +354,9 @@ void ASTInterpreter::doStore(InternedString name, Value value) { setitem(frame_info.boxedLocals, boxString(name.str()), value.o); } else { sym_table[name] = value.o; - if (vst == ScopeInfo::VarScopeType::CLOSURE) - setattr(created_closure, name.c_str(), value.o); + if (vst == ScopeInfo::VarScopeType::CLOSURE) { + created_closure->elts[scope_info->getClosureOffset(name)] = value.o; + } } } @@ -1082,8 +1083,20 @@ Value ASTInterpreter::visit_name(AST_Name* node) { switch (node->lookup_type) { case ScopeInfo::VarScopeType::GLOBAL: return getGlobal(source_info->parent_module, &node->id.str()); - case ScopeInfo::VarScopeType::DEREF: - return getattr(passed_closure, node->id.c_str()); + case ScopeInfo::VarScopeType::DEREF: { + DerefInfo deref_info = scope_info->getDerefInfo(node->id); + assert(passed_closure); + BoxedClosure* closure = passed_closure; + for (int i = 0; i < deref_info.num_parents_from_passed_closure; i++) { + closure = closure->parent; + } + Box* val = closure->elts[deref_info.offset]; + if (val == NULL) { + raiseExcHelper(NameError, "free variable '%s' referenced before assignment in enclosing scope", + node->id.c_str()); + } + return val; + } case ScopeInfo::VarScopeType::FAST: case ScopeInfo::VarScopeType::CLOSURE: { SymMap::iterator it = sym_table.find(node->id); diff --git a/src/codegen/compvars.cpp b/src/codegen/compvars.cpp index 20e6ec5a1..5cf9d68c7 100644 --- a/src/codegen/compvars.cpp +++ b/src/codegen/compvars.cpp @@ -1781,15 +1781,17 @@ class ClosureType : public ConcreteCompilerType { CompilerVariable* getattr(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var, const std::string* attr, bool cls_only) override { + RELEASE_ASSERT(0, "should not be called\n"); + /* assert(!cls_only); llvm::Value* bitcast = emitter.getBuilder()->CreateBitCast(var->getValue(), g.llvm_value_type_ptr); return ConcreteCompilerVariable(UNKNOWN, bitcast, true).getattr(emitter, info, attr, cls_only); + */ } void setattr(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var, const std::string* attr, CompilerVariable* v) override { - llvm::Value* bitcast = emitter.getBuilder()->CreateBitCast(var->getValue(), g.llvm_value_type_ptr); - ConcreteCompilerVariable(UNKNOWN, bitcast, true).setattr(emitter, info, attr, v); + RELEASE_ASSERT(0, "should not be called\n"); } ConcreteCompilerType* getConcreteType() override { return this; } diff --git a/src/codegen/irgen/irgenerator.cpp b/src/codegen/irgen/irgenerator.cpp index c741595ba..53077cd27 100644 --- a/src/codegen/irgen/irgenerator.cpp +++ b/src/codegen/irgen/irgenerator.cpp @@ -898,10 +898,29 @@ class IRGeneratorImpl : public IRGenerator { assert(!is_kill); assert(scope_info->takesClosure()); + DerefInfo deref_info = scope_info->getDerefInfo(node->id); + + static_assert(sizeof(Box) == offsetof(BoxedClosure, parent), ""); + static_assert(offsetof(BoxedClosure, parent) + sizeof(BoxedClosure*) == offsetof(BoxedClosure, nelts), ""); + static_assert(offsetof(BoxedClosure, nelts) + sizeof(size_t) == offsetof(BoxedClosure, elts), ""); + CompilerVariable* closure = symbol_table[internString(PASSED_CLOSURE_NAME)]; - assert(closure); + llvm::Value* closureValue = closure->makeConverted(emitter, CLOSURE)->getValue(); + closure->decvref(emitter); + llvm::Value* gep; + for (int i = 0; i < deref_info.num_parents_from_passed_closure; i++) { + gep = emitter.getBuilder()->CreateConstInBoundsGEP2_32(closureValue, 0, 1); + closureValue = emitter.getBuilder()->CreateLoad(gep); + } + gep = emitter.getBuilder()->CreateGEP(closureValue, + { llvm::ConstantInt::get(g.i32, 0), llvm::ConstantInt::get(g.i32, 3), + llvm::ConstantInt::get(g.i32, deref_info.offset) }); + llvm::Value* lookupResult = emitter.getBuilder()->CreateLoad(gep); - return closure->getattr(emitter, getEmptyOpInfo(unw_info), &node->id.str(), false); + emitter.createCall(unw_info, g.funcs.assertDerefNameDefined, + { lookupResult, getStringConstantPtr(node->id.str() + '\0') }); + + return new ConcreteCompilerVariable(UNKNOWN, lookupResult, true); } else if (vst == ScopeInfo::VarScopeType::NAME) { llvm::Value* boxedLocals = irstate->getBoxedLocalsVar(); llvm::Value* attr = getStringConstantPtr(node->id.str() + '\0'); @@ -1431,10 +1450,22 @@ class IRGeneratorImpl : public IRGenerator { _popFake(defined_name, true); if (vst == ScopeInfo::VarScopeType::CLOSURE) { + size_t offset = scope_info->getClosureOffset(name); + + CompilerVariable* closure = symbol_table[internString(CREATED_CLOSURE_NAME)]; + llvm::Value* closureValue = closure->makeConverted(emitter, CLOSURE)->getValue(); + closure->decvref(emitter); + llvm::Value* gep = emitter.getBuilder()->CreateGEP( + closureValue, { llvm::ConstantInt::get(g.i32, 0), llvm::ConstantInt::get(g.i32, 3), + llvm::ConstantInt::get(g.i32, offset) }); + emitter.getBuilder()->CreateStore(val->makeConverted(emitter, UNKNOWN)->getValue(), gep); + + /* CompilerVariable* closure = symbol_table[internString(CREATED_CLOSURE_NAME)]; assert(closure); closure->setattr(emitter, getEmptyOpInfo(unw_info), &name.str(), val); + */ } } } @@ -1893,7 +1924,8 @@ class IRGeneratorImpl : public IRGenerator { assert(var->getType() != BOXED_FLOAT && "should probably unbox it, but why is it boxed in the first place?"); - // This line can never get hit right now for the same reason that the variables must already be concrete, + // This line can never get hit right now for the same reason that the variables must already be + // concrete, // because we're over-generating phis. ASSERT(var->isGrabbed(), "%s", p.first.c_str()); // var->ensureGrabbed(emitter); @@ -2152,7 +2184,8 @@ class IRGeneratorImpl : public IRGenerator { } else { #ifndef NDEBUG if (myblock->successors.size()) { - // TODO getTypeAtBlockEnd will automatically convert up to the concrete type, which we don't want + // TODO getTypeAtBlockEnd will automatically convert up to the concrete type, which we don't + // want // here, but this is just for debugging so I guess let it happen for now: ConcreteCompilerType* ending_type = types->getTypeAtBlockEnd(it->first, myblock); ASSERT(it->second->canConvertTo(ending_type), "%s is supposed to be %s, but somehow is %s", @@ -2352,7 +2385,8 @@ class IRGeneratorImpl : public IRGenerator { if (!passed_closure) passed_closure = embedConstantPtr(nullptr, g.llvm_closure_type_ptr); - llvm::Value* new_closure = emitter.getBuilder()->CreateCall(g.funcs.createClosure, passed_closure); + llvm::Value* new_closure = emitter.getBuilder()->CreateCall2( + g.funcs.createClosure, passed_closure, getConstantInt(scope_info->getClosureSize(), g.i64)); symbol_table[internString(CREATED_CLOSURE_NAME)] = new ConcreteCompilerVariable(getCreatedClosureType(), new_closure, true); } diff --git a/src/codegen/runtime_hooks.cpp b/src/codegen/runtime_hooks.cpp index 2b037528e..3d3471cbf 100644 --- a/src/codegen/runtime_hooks.cpp +++ b/src/codegen/runtime_hooks.cpp @@ -216,6 +216,7 @@ void initGlobalFuncs(GlobalState& g) { GET(raiseAttributeErrorStr); GET(raiseNotIterableError); GET(assertNameDefined); + GET(assertDerefNameDefined); GET(assertFail); GET(printFloat); diff --git a/src/codegen/runtime_hooks.h b/src/codegen/runtime_hooks.h index 1141c8c7c..f16478d5b 100644 --- a/src/codegen/runtime_hooks.h +++ b/src/codegen/runtime_hooks.h @@ -41,7 +41,7 @@ struct GlobalFuncs { *exceptionMatches, *yield, *getiterHelper, *hasnext; llvm::Value* unpackIntoArray, *raiseAttributeError, *raiseAttributeErrorStr, *raiseNotIterableError, - *assertNameDefined, *assertFail; + *assertNameDefined, *assertFail, *assertDerefNameDefined; llvm::Value* printFloat, *listAppendInternal, *getSysStdout; llvm::Value* runtimeCall0, *runtimeCall1, *runtimeCall2, *runtimeCall3, *runtimeCall, *runtimeCallN; llvm::Value* callattr0, *callattr1, *callattr2, *callattr3, *callattr, *callattrN; diff --git a/src/codegen/unwinding.cpp b/src/codegen/unwinding.cpp index 1afa6e9df..324929576 100644 --- a/src/codegen/unwinding.cpp +++ b/src/codegen/unwinding.cpp @@ -758,18 +758,21 @@ Box* fastLocalsToBoxedLocals() { // Add the locals from the closure // TODO in a ClassDef scope, we aren't supposed to add these - for (; closure != NULL; closure = closure->parent) { - assert(closure->cls == closure_cls); - for (auto& attr_offset : closure->attrs.hcls->getAttrOffsets()) { - const std::string& name = attr_offset.first(); - int offset = attr_offset.second; - Box* val = closure->attrs.attr_list->attrs[offset]; - if (val != NULL && scope_info->isPassedToViaClosure(scope_info->internString(name))) { - Box* boxedName = boxString(name); - if (d->d.count(boxedName) == 0) { - d->d[boxString(name)] = val; - } - } + size_t depth = 0; + for (auto& p : scope_info->getAllDerefVarsAndInfo()) { + InternedString name = p.first; + DerefInfo derefInfo = p.second; + while (depth < derefInfo.num_parents_from_passed_closure) { + depth++; + closure = closure->parent; + } + assert(closure != NULL); + Box* val = closure->elts[derefInfo.offset]; + Box* boxedName = boxString(name.str()); + if (val != NULL) { + d->d[boxedName] = val; + } else { + d->d.erase(boxedName); } } diff --git a/src/runtime/inline/link_forcer.cpp b/src/runtime/inline/link_forcer.cpp index e5102f004..f612cddce 100644 --- a/src/runtime/inline/link_forcer.cpp +++ b/src/runtime/inline/link_forcer.cpp @@ -98,6 +98,7 @@ void force() { FORCE(raiseAttributeErrorStr); FORCE(raiseNotIterableError); FORCE(assertNameDefined); + FORCE(assertDerefNameDefined); FORCE(assertFail); FORCE(printFloat); diff --git a/src/runtime/objmodel.cpp b/src/runtime/objmodel.cpp index d3eb760a4..c12626393 100644 --- a/src/runtime/objmodel.cpp +++ b/src/runtime/objmodel.cpp @@ -233,6 +233,12 @@ extern "C" void assertNameDefined(bool b, const char* name, BoxedClass* exc_cls, } } +extern "C" void assertDerefNameDefined(Box* b, const char* name) { + if (b == NULL) { + raiseExcHelper(NameError, "free variable '%s' referenced before assignment in enclosing scope", name); + } +} + extern "C" void raiseAttributeErrorStr(const char* typeName, const char* attr) { raiseExcHelper(AttributeError, "'%s' object has no attribute '%s'", typeName, attr); } @@ -1274,39 +1280,7 @@ Box* getattrInternalGeneric(Box* obj, const std::string& attr, GetattrRewriteArg } // TODO this should be a custom getattr - if (obj->cls == closure_cls) { - Box* val = NULL; - if (rewrite_args) { - GetattrRewriteArgs hrewrite_args(rewrite_args->rewriter, rewrite_args->obj, rewrite_args->destination); - val = obj->getattr(attr, &hrewrite_args); - - if (!hrewrite_args.out_success) { - rewrite_args = NULL; - } else if (val) { - rewrite_args->out_rtn = hrewrite_args.out_rtn; - rewrite_args->out_success = true; - return val; - } - } else { - val = obj->getattr(attr, NULL); - if (val) { - return val; - } - } - - // If val doesn't exist, then we move up to the parent closure - // TODO closures should get their own treatment, but now just piggy-back on the - // normal hidden-class IC logic. - // Can do better since we don't need to guard on the cls (always going to be closure) - BoxedClosure* closure = static_cast(obj); - if (closure->parent) { - if (rewrite_args) { - rewrite_args->obj = rewrite_args->obj->getAttr(offsetof(BoxedClosure, parent)); - } - return getattrInternal(closure->parent, attr, rewrite_args); - } - raiseExcHelper(NameError, "free variable '%s' referenced before assignment in enclosing scope", attr.c_str()); - } + assert(obj->cls != closure_cls); // Handle descriptor logic here. // A descriptor is either a data descriptor or a non-data descriptor. diff --git a/src/runtime/objmodel.h b/src/runtime/objmodel.h index d7e633690..b1b74bf31 100644 --- a/src/runtime/objmodel.h +++ b/src/runtime/objmodel.h @@ -83,9 +83,10 @@ extern "C" Box* importFrom(Box* obj, const std::string* attr); extern "C" Box* importStar(Box* from_module, BoxedModule* to_module); extern "C" Box** unpackIntoArray(Box* obj, int64_t expected_size); extern "C" void assertNameDefined(bool b, const char* name, BoxedClass* exc_cls, bool local_var_msg); +extern "C" void assertDerefNameDefined(Box* b, const char* name); extern "C" void assertFail(BoxedModule* inModule, Box* msg); extern "C" bool isSubclass(BoxedClass* child, BoxedClass* parent); -extern "C" BoxedClosure* createClosure(BoxedClosure* parent_closure); +extern "C" BoxedClosure* createClosure(BoxedClosure* parent_closure, size_t size); Box* getiter(Box* o); extern "C" Box* getPystonIter(Box* o); diff --git a/src/runtime/types.cpp b/src/runtime/types.cpp index 997f7933f..85d33ec0e 100644 --- a/src/runtime/types.cpp +++ b/src/runtime/types.cpp @@ -617,6 +617,11 @@ extern "C" void closureGCHandler(GCVisitor* v, Box* b) { BoxedClosure* c = (BoxedClosure*)b; if (c->parent) v->visit(c->parent); + + for (int i = 0; i < c->nelts; i++) { + if (c->elts[i]) + v->visit(c->elts[i]); + } } extern "C" { @@ -830,10 +835,12 @@ extern "C" Box* createSlice(Box* start, Box* stop, Box* step) { return rtn; } -extern "C" BoxedClosure* createClosure(BoxedClosure* parent_closure) { +extern "C" BoxedClosure* createClosure(BoxedClosure* parent_closure, size_t n) { if (parent_closure) assert(parent_closure->cls == closure_cls); - return new BoxedClosure(parent_closure); + BoxedClosure* closure = new (n) BoxedClosure(parent_closure); + assert(closure->cls == closure_cls); + return closure; } extern "C" Box* sliceNew(Box* cls, Box* start, Box* stop, Box** args) { @@ -2002,8 +2009,8 @@ void setupRuntime() { sizeof(BoxedSet), false, "frozenset"); capi_getset_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, 0, 0, sizeof(BoxedGetsetDescriptor), false, "getset"); - closure_cls = BoxedHeapClass::create(type_cls, object_cls, &closureGCHandler, offsetof(BoxedClosure, attrs), 0, - sizeof(BoxedClosure), false, "closure"); + closure_cls + = BoxedHeapClass::create(type_cls, object_cls, &closureGCHandler, 0, 0, sizeof(BoxedClosure), false, "closure"); property_cls = BoxedHeapClass::create(type_cls, object_cls, &propertyGCHandler, 0, 0, sizeof(BoxedProperty), false, "property"); staticmethod_cls = BoxedHeapClass::create(type_cls, object_cls, &staticmethodGCHandler, 0, 0, diff --git a/src/runtime/types.h b/src/runtime/types.h index 7a492243b..1189cd088 100644 --- a/src/runtime/types.h +++ b/src/runtime/types.h @@ -618,15 +618,27 @@ class BoxedClassmethod : public Box { DEFAULT_CLASS_SIMPLE(classmethod_cls); }; -// TODO is there any particular reason to make this a Box, ie a python-level object? +// TODO is there any particular reason to make this a Box, i.e. a python-level object? class BoxedClosure : public Box { public: - HCAttrs attrs; BoxedClosure* parent; + size_t nelts; + Box* elts[0]; BoxedClosure(BoxedClosure* parent) : parent(parent) {} - DEFAULT_CLASS(closure_cls); + void* operator new(size_t size, size_t nelts) __attribute__((visibility("default"))) { + /* + BoxedClosure* rtn + = static_cast(gc_alloc(_PyObject_VAR_SIZE(closure_cls, nelts), gc::GCKind::PYTHON)); + */ + BoxedClosure* rtn + = static_cast(gc_alloc(sizeof(BoxedClosure) + nelts * sizeof(Box*), gc::GCKind::PYTHON)); + rtn->nelts = nelts; + rtn->cls = closure_cls; + memset((void*)rtn->elts, 0, sizeof(Box*) * nelts); + return rtn; + } }; class BoxedGenerator : public Box { diff --git a/test/tests/static_closure_locations.py b/test/tests/static_closure_locations.py new file mode 100644 index 000000000..0530108aa --- /dev/null +++ b/test/tests/static_closure_locations.py @@ -0,0 +1,25 @@ +# should_error + +# The use of c makes sure a closure gets passed through all 4 functions. +# The use of a in g makes sure that a is in f's closure. +# The a in j should refer to the a in h, thus throwing an exception since +# it is undefined (that is, it should *not* access the a from f even +# though it access via the closure). + +def f(): + c = 0 + a = 0 + def g(): + print c + print a + def h(): + print c + def j(): + print c + print a + + j() + a = 1 + h() + g() +f()