Commit 55805dc6 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #349 from tjhance/exec-basic

Conflicts:
	src/codegen/ast_interpreter.cpp
	src/codegen/irgen/irgenerator.cpp
	src/codegen/pypa-parser.cpp
parents 9e30d6dc 2bfcd7d1
......@@ -340,6 +340,8 @@ public:
return true;
}
virtual bool visit_exec(AST_Exec* node) { return true; }
friend class DefinednessBBAnalyzer;
};
......@@ -376,7 +378,8 @@ DefinednessAnalysis::DefinednessAnalysis(const ParamNames& arg_names, CFG* cfg,
for (const auto& p : results) {
RequiredSet required;
for (const auto& p2 : p.second) {
if (scope_info->refersToGlobal(p2.first))
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(p2.first);
if (vst == ScopeInfo::VarScopeType::GLOBAL || vst == ScopeInfo::VarScopeType::NAME)
continue;
// printf("%d %s %d\n", p.first->idx, p2.first.c_str(), p2.second);
......
......@@ -18,6 +18,7 @@
#include "core/ast.h"
#include "core/common.h"
#include "core/types.h"
#include "core/util.h"
namespace pyston {
......@@ -90,24 +91,20 @@ public:
ScopeInfo* getParent() override { return NULL; }
bool createsClosure() override { return false; }
bool takesClosure() override { return false; }
bool passesThroughClosure() override { return false; }
bool refersToGlobal(InternedString name) override {
if (isCompilerCreatedName(name))
return false;
// assert(name[0] != '#' && "should test this");
return true;
}
bool refersToClosure(InternedString name) override { return false; }
bool saveInClosure(InternedString name) override { return false; }
VarScopeType getScopeTypeOfName(InternedString name) override {
return refersToGlobal(name) ? VarScopeType::GLOBAL : VarScopeType::FAST;
if (isCompilerCreatedName(name))
return VarScopeType::FAST;
else
return VarScopeType::GLOBAL;
}
bool usesNameLookup() override { return false; }
bool isPassedToViaClosure(InternedString name) override { return false; }
InternedString mangleName(InternedString id) override { return id; }
InternedString internString(llvm::StringRef s) override { abort(); }
};
......@@ -130,8 +127,27 @@ struct ScopingAnalysis::ScopeNameUsage {
StrSet got_from_closure;
StrSet passthrough_accesses; // what names a child scope accesses a name from a parent scope
// `import *` and `exec` both force the scope to use the NAME lookup
// However, this is not allowed to happen (a SyntaxError) if the scope
// "free variables", variables read but not written (and not forced to be global)
// Furthermore, no child of the scope can have any free variables either
// (not even if the variables would refer to a closure in an in-between child).
AST_ImportFrom* nameForcingNodeImportStar;
AST_Exec* nameForcingNodeBareExec;
bool hasNameForcingSyntax() { return nameForcingNodeImportStar != NULL || nameForcingNodeBareExec != NULL; }
// If it has a free variable / if any child has a free variable
// `free` is set to true if there is a variable which is read but not written,
// unless there is a `global` statement (possibly in a parent scope--but note
// that `forced_globals` only contains the `global` statements in *this* scope).
// See the computation in process_name_usages below for specifics.
// `child_free` is then set on any parent scopes of a scope that has `free` set.
bool free;
bool child_free;
ScopeNameUsage(AST* node, ScopeNameUsage* parent, ScopingAnalysis* scoping)
: node(node), parent(parent), scoping(scoping) {
: node(node), parent(parent), scoping(scoping), nameForcingNodeImportStar(NULL), nameForcingNodeBareExec(NULL),
free(false), child_free(false) {
if (node->type == AST_TYPE::ClassDef) {
AST_ClassDef* classdef = ast_cast<AST_ClassDef>(node);
......@@ -177,13 +193,11 @@ private:
ScopeInfo* parent;
ScopingAnalysis::ScopeNameUsage* usage;
AST* ast;
bool usesNameLookup;
bool usesNameLookup_;
public:
ScopeInfoBase(ScopeInfo* parent, ScopingAnalysis::ScopeNameUsage* usage, AST* ast, bool usesNameLookup)
: parent(parent), usage(usage), ast(ast), usesNameLookup(usesNameLookup) {
// not true anymore: Expression
// assert(parent);
: parent(parent), usage(usage), ast(ast), usesNameLookup_(usesNameLookup) {
assert(usage);
assert(ast);
}
......@@ -200,44 +214,36 @@ public:
bool passesThroughClosure() override { return usage->passthrough_accesses.size() > 0 && !createsClosure(); }
bool refersToGlobal(InternedString name) override {
// HAX
if (isCompilerCreatedName(name))
return false;
if (usage->forced_globals.count(name))
return true;
if (usesNameLookup)
return false;
return usage->written.count(name) == 0 && usage->got_from_closure.count(name) == 0;
}
bool refersToClosure(InternedString name) override {
// HAX
if (isCompilerCreatedName(name))
return false;
return usage->got_from_closure.count(name) != 0;
}
bool saveInClosure(InternedString name) override {
// HAX
if (isCompilerCreatedName(name) || usesNameLookup)
return false;
return usage->referenced_from_nested.count(name) != 0;
}
VarScopeType getScopeTypeOfName(InternedString name) override {
// HAX
if (isCompilerCreatedName(name))
return VarScopeType::FAST;
if (refersToClosure(name))
if (usage->forced_globals.count(name) > 0)
return VarScopeType::GLOBAL;
if (usage->got_from_closure.count(name) > 0)
return VarScopeType::DEREF;
if (refersToGlobal(name))
if (usesNameLookup_) {
return VarScopeType::NAME;
} else {
if (usage->written.count(name) == 0)
return VarScopeType::GLOBAL;
if (saveInClosure(name))
else if (usage->referenced_from_nested.count(name) > 0)
return VarScopeType::CLOSURE;
if (usesNameLookup)
return VarScopeType::NAME;
else
return VarScopeType::FAST;
}
}
bool usesNameLookup() override { return usesNameLookup_; }
bool isPassedToViaClosure(InternedString name) override {
if (isCompilerCreatedName(name))
return false;
return usage->got_from_closure.count(name) > 0 || usage->passthrough_accesses.count(name) > 0;
}
InternedString mangleName(const InternedString id) override {
return pyston::mangleName(id, usage->private_name, usage->scoping->getInternedStrings());
......@@ -271,6 +277,16 @@ public:
cur->read.insert(name);
}
void doImportStar(AST_ImportFrom* node) {
if (cur->nameForcingNodeImportStar == NULL)
cur->nameForcingNodeImportStar = node;
}
void doBareExec(AST_Exec* node) {
if (cur->nameForcingNodeBareExec == NULL)
cur->nameForcingNodeBareExec = node;
}
bool visit_name(AST_Name* node) override {
mangleNameInPlace(node->id, cur->private_name, scoping->getInternedStrings());
......@@ -314,6 +330,7 @@ public:
bool visit_list(AST_List* node) override { return false; }
bool visit_listcomp(AST_ListComp* node) override { return false; }
bool visit_expression(AST_Expression* node) override { return false; }
bool visit_suite(AST_Suite* node) override { return false; }
// bool visit_module(AST_Module *node) override { return false; }
// bool visit_name(AST_Name *node) override { return false; }
bool visit_num(AST_Num* node) override { return false; }
......@@ -468,8 +485,13 @@ public:
}
bool visit_importfrom(AST_ImportFrom* node) override {
mangleNameInPlace(node->module, cur->private_name, scoping->getInternedStrings());
for (int i = 0; i < node->names.size(); i++) {
AST_alias* alias = node->names[i];
if (alias->name.str() == std::string("*")) {
mangleNameInPlace(alias->asname, cur->private_name, scoping->getInternedStrings());
doImportStar(node);
} else {
mangleNameInPlace(alias->asname, cur->private_name, scoping->getInternedStrings());
mangleNameInPlace(alias->name, cur->private_name, scoping->getInternedStrings());
if (alias->asname.str().size())
......@@ -477,9 +499,17 @@ public:
else
doWrite(alias->name);
}
}
return true;
}
bool visit_exec(AST_Exec* node) override {
if (node->locals == NULL) {
doBareExec(node);
}
return false;
}
static void collect(AST* node, ScopingAnalysis::NameUsageMap* map, ScopingAnalysis* scoping) {
assert(map);
assert(map->count(node));
......@@ -514,16 +544,44 @@ static std::vector<ScopingAnalysis::ScopeNameUsage*> sortNameUsages(ScopingAnaly
return rtn;
}
static void raiseNameForcingSyntaxError(const char* msg, ScopingAnalysis::ScopeNameUsage* usage) {
assert(usage->node->type == AST_TYPE::FunctionDef);
AST_FunctionDef* funcNode = static_cast<AST_FunctionDef*>(usage->node);
int lineno;
const char* syntaxElemMsg;
if (usage->nameForcingNodeImportStar && usage->nameForcingNodeBareExec) {
syntaxElemMsg = "function '%s' uses import * and bare exec, which are illegal because it %s";
lineno = std::min(usage->nameForcingNodeImportStar->lineno, usage->nameForcingNodeBareExec->lineno);
} else if (usage->nameForcingNodeImportStar) {
syntaxElemMsg = "import * is not allowed in function '%s' because it %s";
lineno = usage->nameForcingNodeImportStar->lineno;
} else {
syntaxElemMsg = "unqualified exec is not allowed in function '%.100s' it %s";
lineno = usage->nameForcingNodeBareExec->lineno;
}
char buf[1024];
snprintf(buf, sizeof(buf), syntaxElemMsg, funcNode->name.c_str(), msg);
raiseSyntaxError(buf, lineno, 0, "" /* file?? */, funcNode->name.str());
}
void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) {
// Resolve name lookups:
for (const auto& p : *usages) {
ScopeNameUsage* usage = p.second;
bool is_any_name_free = false;
for (const auto& name : usage->read) {
if (usage->forced_globals.count(name))
continue;
if (usage->written.count(name))
continue;
bool is_name_free = true;
std::vector<ScopeNameUsage*> intermediate_parents;
ScopeNameUsage* parent = usage->parent;
......@@ -532,6 +590,7 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) {
intermediate_parents.push_back(parent);
parent = parent->parent;
} else if (parent->forced_globals.count(name)) {
is_name_free = false;
break;
} else if (parent->written.count(name)) {
usage->got_from_closure.insert(name);
......@@ -547,9 +606,33 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) {
parent = parent->parent;
}
}
if (is_name_free)
is_any_name_free = true;
}
if (is_any_name_free) {
// This intentionally loops through *all* parents, not just the ones in intermediate_parents
// Label any parent FunctionDef as `child_free`, and if such a parent exists, also label
// this node as `free` itself.
for (ScopeNameUsage* parent = usage->parent; parent != NULL; parent = parent->parent) {
if (parent->node->type == AST_TYPE::FunctionDef) {
usage->free = true;
parent->child_free = true;
}
}
}
}
for (const auto& p : *usages) {
ScopeNameUsage* usage = p.second;
if (usage->hasNameForcingSyntax()) {
if (usage->child_free)
raiseNameForcingSyntaxError("contains a nested function with free variables", usage);
else if (usage->free)
raiseNameForcingSyntaxError("is a nested function", usage);
}
}
std::vector<ScopeNameUsage*> sorted_usages = sortNameUsages(usages);
......@@ -562,6 +645,7 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) {
switch (node->type) {
case AST_TYPE::Expression:
case AST_TYPE::Suite:
case AST_TYPE::ClassDef: {
ScopeInfoBase* scopeInfo
= new ScopeInfoBase(parent_info, usage, usage->node, true /* usesNameLookup */);
......@@ -573,8 +657,8 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) {
case AST_TYPE::GeneratorExp:
case AST_TYPE::DictComp:
case AST_TYPE::SetComp: {
ScopeInfoBase* scopeInfo
= new ScopeInfoBase(parent_info, usage, usage->node, false /* usesNameLookup */);
ScopeInfoBase* scopeInfo = new ScopeInfoBase(parent_info, usage, usage->node,
usage->hasNameForcingSyntax() /* usesNameLookup */);
this->scopes[node] = scopeInfo;
break;
}
......@@ -634,12 +718,13 @@ ScopingAnalysis::ScopingAnalysis(AST_Module* m) : parent_module(m), interned_str
scopes[m] = new ModuleScopeInfo();
}
ScopingAnalysis* runScopingAnalysis(AST_Module* m) {
return new ScopingAnalysis(m);
}
ScopingAnalysis::ScopingAnalysis(AST_Expression* e) : interned_strings(*e->interned_strings.get()) {
auto scope_info = getScopeInfoForNode(e);
scopes[e] = scope_info;
}
ScopingAnalysis::ScopingAnalysis(AST_Suite* s) : interned_strings(*s->interned_strings.get()) {
auto scope_info = getScopeInfoForNode(s);
scopes[s] = scope_info;
}
}
......@@ -23,6 +23,7 @@ namespace pyston {
class AST;
class AST_Module;
class AST_Expression;
class AST_Suite;
class ScopeInfo {
public:
......@@ -61,13 +62,58 @@ public:
// import dis
// print dis.dis(g)
enum class VarScopeType { FAST, GLOBAL, CLOSURE, DEREF, NAME };
virtual bool refersToGlobal(InternedString name) = 0;
virtual bool refersToClosure(InternedString name) = 0;
virtual bool saveInClosure(InternedString name) = 0;
enum class VarScopeType {
FAST,
GLOBAL,
CLOSURE,
DEREF,
NAME,
// This is never returned by any function in this class, but it is used by
// the ast_interpreter currently.
UNKNOWN
};
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
// `exec` or `eval` scope.
virtual bool usesNameLookup() = 0;
virtual InternedString mangleName(InternedString id) = 0;
virtual InternedString internString(llvm::StringRef) = 0;
};
......@@ -99,13 +145,12 @@ public:
ScopingAnalysis(AST_Module* m);
ScopingAnalysis(AST_Expression* e);
ScopingAnalysis(AST_Suite* s);
ScopeInfo* getScopeInfoForNode(AST* node);
InternedStringPool& getInternedStrings();
};
ScopingAnalysis* runScopingAnalysis(AST_Module* m);
bool containsYield(AST* ast);
}
......
......@@ -590,6 +590,14 @@ private:
_visit_alias(alias);
}
void visit_exec(AST_Exec* node) override {
getType(node->body);
if (node->globals)
getType(node->globals);
if (node->locals)
getType(node->locals);
}
void visit_invoke(AST_Invoke* node) override { node->stmt->accept_stmt(this); }
void visit_jump(AST_Jump* node) override {}
......
......@@ -90,6 +90,7 @@ private:
Value visit_call(AST_Call* node);
Value visit_compare(AST_Compare* node);
Value visit_delete(AST_Delete* node);
Value visit_exec(AST_Exec* node);
Value visit_global(AST_Global* node);
Value visit_module(AST_Module* node);
Value visit_print(AST_Print* node);
......@@ -152,16 +153,47 @@ public:
const ScopeInfo* getScopeInfo() { return scope_info; }
void addSymbol(InternedString name, Box* value, bool allow_duplicates);
void setGenerator(Box* gen);
void setPassedClosure(Box* closure);
void setCreatedClosure(Box* closure);
void setBoxedLocals(Box*);
void setFrameInfo(const FrameInfo* frame_info);
void gcVisit(GCVisitor* visitor);
};
void ASTInterpreter::addSymbol(InternedString name, Box* value, bool allow_duplicates) {
if (!allow_duplicates)
assert(sym_table.count(name) == 0);
sym_table[name] = value;
}
void ASTInterpreter::setGenerator(Box* gen) {
assert(!this->generator); // This should only used for initialization
assert(gen->cls == generator_cls);
this->generator = static_cast<BoxedGenerator*>(gen);
}
void ASTInterpreter::setPassedClosure(Box* closure) {
assert(!this->passed_closure); // This should only used for initialization
assert(closure->cls == closure_cls);
this->passed_closure = static_cast<BoxedClosure*>(closure);
}
void ASTInterpreter::setCreatedClosure(Box* closure) {
assert(!this->created_closure); // This should only used for initialization
assert(closure->cls == closure_cls);
this->created_closure = static_cast<BoxedClosure*>(closure);
}
void ASTInterpreter::setBoxedLocals(Box* boxedLocals) {
this->frame_info.boxedLocals = boxedLocals;
}
void ASTInterpreter::setFrameInfo(const FrameInfo* frame_info) {
this->frame_info = *frame_info;
}
void ASTInterpreter::gcVisit(GCVisitor* visitor) {
for (const auto& p2 : getSymbolTable()) {
visitor->visitPotential(p2.second);
......@@ -173,6 +205,8 @@ void ASTInterpreter::gcVisit(GCVisitor* visitor) {
visitor->visit(created_closure);
if (generator)
visitor->visit(generator);
if (frame_info.boxedLocals)
visitor->visit(frame_info.boxedLocals);
}
ASTInterpreter::ASTInterpreter(CompiledFunction* compiled_function)
......@@ -285,7 +319,7 @@ void ASTInterpreter::eraseDeadSymbols() {
if (!source_info->liveness->isLiveAtEnd(it.first, current_block)) {
dead_symbols.push_back(it.first);
} else if (source_info->phis->isRequiredAfter(it.first, current_block)) {
assert(!scope_info->refersToGlobal(it.first));
assert(scope_info->getScopeTypeOfName(it.first) != ScopeInfo::VarScopeType::GLOBAL);
} else {
}
}
......@@ -311,11 +345,16 @@ Value ASTInterpreter::doBinOp(Box* left, Box* right, int op, BinExpType exp_type
}
void ASTInterpreter::doStore(InternedString name, Value value) {
if (scope_info->refersToGlobal(name)) {
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(name);
if (vst == ScopeInfo::VarScopeType::GLOBAL) {
setattr(source_info->parent_module, name.c_str(), value.o);
} else if (vst == ScopeInfo::VarScopeType::NAME) {
assert(frame_info.boxedLocals != NULL);
// TODO should probably pre-box the names when it's a scope that usesNameLookup
setitem(frame_info.boxedLocals, boxString(name.str()), value.o);
} else {
sym_table[name] = value.o;
if (scope_info->saveInClosure(name))
if (vst == ScopeInfo::VarScopeType::CLOSURE)
setattr(created_closure, name.c_str(), value.o);
}
}
......@@ -433,6 +472,8 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
if (created_closure)
sorted_symbol_table[source_info->getInternedStrings().get(CREATED_CLOSURE_NAME)] = created_closure;
sorted_symbol_table[source_info->getInternedStrings().get(FRAME_INFO_PTR_NAME)] = (Box*)&frame_info;
if (found_entry == nullptr) {
OSREntryDescriptor* entry = OSREntryDescriptor::create(compiled_func, node);
......@@ -443,6 +484,8 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
entry->args[it.first] = GENERATOR;
else if (it.first.str() == PASSED_CLOSURE_NAME || it.first.str() == CREATED_CLOSURE_NAME)
entry->args[it.first] = CLOSURE;
else if (it.first.str() == FRAME_INFO_PTR_NAME)
entry->args[it.first] = FRAME_INFO;
else {
assert(it.first.str()[0] != '!');
entry->args[it.first] = UNKNOWN;
......@@ -548,16 +591,8 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
v = boxBool(exceptionMatches(obj.o, cls.o));
} else if (node->opcode == AST_LangPrimitive::LOCALS) {
assert(node->args.size() == 0);
BoxedDict* dict = new BoxedDict;
for (auto& p : sym_table) {
auto s = p.first;
if (s.str()[0] == '!' || s.str()[0] == '#')
continue;
dict->d[new BoxedString(s.str())] = p.second;
}
v = dict;
assert(frame_info.boxedLocals != NULL);
v = frame_info.boxedLocals;
} else if (node->opcode == AST_LangPrimitive::NONZERO) {
assert(node->args.size() == 1);
Value obj = visit_expr(node->args[0]);
......@@ -607,6 +642,8 @@ Value ASTInterpreter::visit_stmt(AST_stmt* node) {
return visit_assign((AST_Assign*)node);
case AST_TYPE::Delete:
return visit_delete((AST_Delete*)node);
case AST_TYPE::Exec:
return visit_exec((AST_Exec*)node);
case AST_TYPE::Expr:
return visit_expr((AST_Expr*)node);
case AST_TYPE::Pass:
......@@ -775,25 +812,31 @@ Value ASTInterpreter::visit_delete(AST_Delete* node) {
}
case AST_TYPE::Name: {
AST_Name* target = (AST_Name*)target_;
if (scope_info->refersToGlobal(target->id)) {
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(target->id);
if (vst == ScopeInfo::VarScopeType::GLOBAL) {
// Can't use delattr since the errors are different:
delGlobal(source_info->parent_module, &target->id.str());
continue;
}
assert(!scope_info->refersToClosure(target->id));
assert(!scope_info->saveInClosure(
target->id)); // SyntaxError: can not delete variable 'x' referenced in nested scope
// A del of a missing name generates different error messages in a function scope vs a classdef scope
bool local_error_msg = (source_info->ast->type != AST_TYPE::ClassDef);
} else if (vst == ScopeInfo::VarScopeType::NAME) {
assert(frame_info.boxedLocals != NULL);
assert(frame_info.boxedLocals->cls == dict_cls);
auto& d = static_cast<BoxedDict*>(frame_info.boxedLocals)->d;
auto it = d.find(boxString(target->id.str()));
if (it == d.end()) {
assertNameDefined(0, target->id.c_str(), NameError, false /* local_var_msg */);
}
d.erase(it);
} else {
assert(vst == ScopeInfo::VarScopeType::FAST);
if (sym_table.count(target->id) == 0) {
assertNameDefined(0, target->id.c_str(), NameError, local_error_msg);
assertNameDefined(0, target->id.c_str(), NameError, true /* local_var_msg */);
return Value();
}
sym_table.erase(target->id);
}
break;
}
default:
......@@ -841,6 +884,17 @@ Value ASTInterpreter::visit_print(AST_Print* node) {
return Value();
}
Value ASTInterpreter::visit_exec(AST_Exec* node) {
RELEASE_ASSERT(!node->globals, "do not support exec with globals or locals yet");
assert(!node->locals);
// TODO implement the locals and globals arguments
Box* code = visit_expr(node->body).o;
exec(code);
return Value();
}
Value ASTInterpreter::visit_compare(AST_Compare* node) {
RELEASE_ASSERT(node->comparators.size() == 1, "not implemented");
return doBinOp(visit_expr(node->left).o, visit_expr(node->comparators[0]).o, node->ops[0], BinExpType::Compare);
......@@ -1021,38 +1075,17 @@ Value ASTInterpreter::visit_str(AST_Str* node) {
}
Value ASTInterpreter::visit_name(AST_Name* node) {
switch (node->lookup_type) {
case AST_Name::UNKNOWN: {
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(node->id);
if (vst == ScopeInfo::VarScopeType::GLOBAL) {
node->lookup_type = AST_Name::GLOBAL;
return getGlobal(source_info->parent_module, &node->id.str());
} else if (vst == ScopeInfo::VarScopeType::DEREF) {
node->lookup_type = AST_Name::CLOSURE;
return getattr(passed_closure, node->id.c_str());
} else {
bool is_old_local = (vst == ScopeInfo::VarScopeType::NAME);
node->lookup_type = is_old_local ? AST_Name::LOCAL : AST_Name::FAST_LOCAL;
SymMap::iterator it = sym_table.find(node->id);
if (it != sym_table.end()) {
Box* value = it->second;
return value;
if (node->lookup_type == ScopeInfo::VarScopeType::UNKNOWN) {
node->lookup_type = scope_info->getScopeTypeOfName(node->id);
}
// classdefs (and some other cases like eval) have different scoping rules than functions:
if (is_old_local)
return getGlobal(source_info->parent_module, &node->id.str());
assertNameDefined(0, node->id.c_str(), UnboundLocalError, true);
return Value();
}
}
case AST_Name::GLOBAL:
switch (node->lookup_type) {
case ScopeInfo::VarScopeType::GLOBAL:
return getGlobal(source_info->parent_module, &node->id.str());
case AST_Name::CLOSURE:
case ScopeInfo::VarScopeType::DEREF:
return getattr(passed_closure, node->id.c_str());
case AST_Name::FAST_LOCAL: {
case ScopeInfo::VarScopeType::FAST:
case ScopeInfo::VarScopeType::CLOSURE: {
SymMap::iterator it = sym_table.find(node->id);
if (it != sym_table.end())
return it->second;
......@@ -1060,9 +1093,11 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
assertNameDefined(0, node->id.c_str(), UnboundLocalError, true);
return Value();
}
case AST_Name::LOCAL: {
SymMap::iterator it = sym_table.find(node->id);
if (it != sym_table.end()) {
case ScopeInfo::VarScopeType::NAME: {
assert(frame_info.boxedLocals->cls == dict_cls);
auto& d = static_cast<BoxedDict*>(frame_info.boxedLocals)->d;
auto it = d.find(boxString(node->id.str()));
if (it != d.end()) {
Box* value = it->second;
return value;
}
......@@ -1119,6 +1154,9 @@ Box* astInterpretFunction(CompiledFunction* cf, int nargs, Box* closure, Box* ge
++cf->times_called;
ASTInterpreter interpreter(cf);
if (unlikely(cf->clfunc->source->getScopeInfo()->usesNameLookup())) {
interpreter.setBoxedLocals(new BoxedDict());
}
interpreter.initArguments(nargs, (BoxedClosure*)closure, (BoxedGenerator*)generator, arg1, arg2, arg3, args);
Value v = ASTInterpreter::execute(interpreter);
......@@ -1126,39 +1164,44 @@ Box* astInterpretFunction(CompiledFunction* cf, int nargs, Box* closure, Box* ge
return v.o ? v.o : None;
}
Box* astInterpretFunctionEval(CompiledFunction* cf, BoxedDict* locals) {
Box* astInterpretFunctionEval(CompiledFunction* cf, Box* boxedLocals) {
++cf->times_called;
ASTInterpreter interpreter(cf);
for (const auto& p : locals->d) {
assert(p.first->cls == str_cls);
auto name = static_cast<BoxedString*>(p.first)->s;
InternedString interned = cf->clfunc->source->getInternedStrings().get(name);
interpreter.addSymbol(interned, p.second, false);
}
interpreter.initArguments(0, NULL, NULL, NULL, NULL, NULL, NULL);
RELEASE_ASSERT(boxedLocals->cls == dict_cls, "we don't support non-dicts here yet");
interpreter.setBoxedLocals(boxedLocals);
Value v = ASTInterpreter::execute(interpreter);
return v.o ? v.o : None;
}
Box* astInterpretFrom(CompiledFunction* cf, AST_expr* after_expr, AST_stmt* enclosing_stmt, Box* expr_val,
BoxedDict* locals) {
FrameStackState frame_state) {
assert(cf);
assert(enclosing_stmt);
assert(locals);
assert(frame_state.locals);
assert(after_expr);
assert(expr_val);
ASTInterpreter interpreter(cf);
for (const auto& p : locals->d) {
for (const auto& p : frame_state.locals->d) {
assert(p.first->cls == str_cls);
auto name = static_cast<BoxedString*>(p.first)->s;
std::string name = static_cast<BoxedString*>(p.first)->s;
if (name == PASSED_GENERATOR_NAME) {
interpreter.setGenerator(p.second);
} else if (name == PASSED_CLOSURE_NAME) {
interpreter.setPassedClosure(p.second);
} else if (name == CREATED_CLOSURE_NAME) {
interpreter.setCreatedClosure(p.second);
} else {
InternedString interned = cf->clfunc->source->getInternedStrings().get(name);
interpreter.addSymbol(interned, p.second, false);
}
}
interpreter.setFrameInfo(frame_state.frame_info);
CFGBlock* start_block = NULL;
AST_stmt* starting_statement = NULL;
......
......@@ -15,6 +15,8 @@
#ifndef PYSTON_CODEGEN_ASTINTERPRETER_H
#define PYSTON_CODEGEN_ASTINTERPRETER_H
#include "codegen/unwinding.h"
namespace pyston {
namespace gc {
......@@ -33,9 +35,9 @@ extern const void* interpreter_instr_addr;
Box* astInterpretFunction(CompiledFunction* f, int nargs, Box* closure, Box* generator, Box* arg1, Box* arg2, Box* arg3,
Box** args);
Box* astInterpretFunctionEval(CompiledFunction* cf, BoxedDict* locals);
Box* astInterpretFunctionEval(CompiledFunction* cf, Box* boxedLocals);
Box* astInterpretFrom(CompiledFunction* cf, AST_expr* after_expr, AST_stmt* enclosing_stmt, Box* expr_val,
BoxedDict* locals);
FrameStackState frame_state);
AST_stmt* getCurrentStatementForInterpretedFrame(void* frame_ptr);
CompiledFunction* getCFForInterpretedFrame(void* frame_ptr);
......
......@@ -41,6 +41,7 @@ SourceInfo::SourceInfo(BoxedModule* m, ScopingAnalysis* scoping, AST* ast, const
case AST_TYPE::Lambda:
case AST_TYPE::Module:
case AST_TYPE::Expression:
case AST_TYPE::Suite:
is_generator = false;
break;
case AST_TYPE::FunctionDef:
......
......@@ -70,7 +70,7 @@ struct GlobalState {
llvm::Type* llvm_class_type, *llvm_class_type_ptr;
llvm::Type* llvm_opaque_type;
llvm::Type* llvm_str_type_ptr;
llvm::Type* frame_info_type;
llvm::Type* llvm_frame_info_type;
llvm::Type* llvm_clfunction_type_ptr, *llvm_closure_type_ptr, *llvm_generator_type_ptr;
llvm::Type* llvm_module_type_ptr, *llvm_bool_type_ptr;
llvm::Type* llvm_excinfo_type;
......
......@@ -1822,11 +1822,32 @@ public:
Box* deserializeFromFrame(const FrameVals& vals) override {
assert(vals.size() == numFrameArgs());
abort();
return reinterpret_cast<Box*>(vals[0]);
}
} _GENERATOR;
ConcreteCompilerType* GENERATOR = &_GENERATOR;
class FrameInfoType : public ConcreteCompilerType {
public:
llvm::Type* llvmType() override { return g.llvm_frame_info_type->getPointerTo(); }
std::string debugName() override { return "FrameInfo"; }
ConcreteCompilerType* getConcreteType() override { return this; }
ConcreteCompilerType* getBoxType() override { return FRAME_INFO; }
void drop(IREmitter& emitter, VAR* var) override {
// pass
}
void grab(IREmitter& emitter, VAR* var) override {
// pass
}
Box* deserializeFromFrame(const FrameVals& vals) override {
RELEASE_ASSERT(false, "should not be called"); // This function shouldn't be called.
}
} _FRAME_INFO;
ConcreteCompilerType* FRAME_INFO = &_FRAME_INFO;
class StrConstantType : public ValuedCompilerType<const std::string*> {
public:
std::string debugName() override { return "str_constant"; }
......
......@@ -415,6 +415,9 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
ptr = entry_emitter->getBuilder()->CreateBitCast(ptr, g.llvm_generator_type_ptr->getPointerTo());
} else if (p.second == CLOSURE) {
ptr = entry_emitter->getBuilder()->CreateBitCast(ptr, g.llvm_closure_type_ptr->getPointerTo());
} else if (p.second == FRAME_INFO) {
ptr = entry_emitter->getBuilder()->CreateBitCast(
ptr, g.llvm_frame_info_type->getPointerTo()->getPointerTo());
} else {
assert(p.second->llvmType() == g.llvm_value_type_ptr);
}
......@@ -422,6 +425,13 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
assert(from_arg->getType() == p.second->llvmType());
}
if (from_arg->getType() == g.llvm_frame_info_type->getPointerTo()) {
assert(p.first.str() == FRAME_INFO_PTR_NAME);
irstate->setFrameInfoArgument(from_arg);
// Don't add the frame info to the symbol table since we will store it separately:
continue;
}
ConcreteCompilerType* phi_type;
phi_type = getTypeAtBlockStart(types, p.first, target_block);
......@@ -595,6 +605,11 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
assert(phis);
for (const auto& p : entry_descriptor->args) {
// Don't add the frame info to the symbol table since we will store it separately
// (we manually added it during the calculation of osr_syms):
if (p.first.str() == FRAME_INFO_PTR_NAME)
continue;
ConcreteCompilerType* analyzed_type = getTypeAtBlockStart(types, p.first, block);
// printf("For %s, given %s, analyzed for %s\n", p.first.c_str(), p.second->debugName().c_str(),
......@@ -634,7 +649,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
if (source->is_generator)
names.insert(source->getInternedStrings().get(PASSED_GENERATOR_NAME));
for (const auto& s : names) {
for (const InternedString& s : names) {
// printf("adding guessed phi for %s\n", s.c_str());
ConcreteCompilerType* type = getTypeAtBlockStart(types, s, block);
llvm::PHINode* phi
......@@ -698,7 +713,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
// You might think I need to check whether `name' is being assigned globally or locally,
// since a global assign doesn't affect the symbol table. However, the CFG pass only
// generates invoke-assigns to temporary variables. Just to be sure, we assert:
assert(!source->getScopeInfo()->refersToGlobal(name));
assert(source->getScopeInfo()->getScopeTypeOfName(name) != ScopeInfo::VarScopeType::GLOBAL);
// TODO: inefficient
sym_table = new SymbolTable(*sym_table);
......
......@@ -31,39 +31,12 @@ const std::map<std::string, FutureOption> future_options
{ "nested_scopes", { version_hex(2, 1, 0), version_hex(2, 2, 0), FF_NESTED_SCOPES } },
{ "with_statement", { version_hex(2, 5, 0), version_hex(3, 6, 0), FF_WITH_STATEMENT } } };
// Helper function:
void raiseSyntaxError(const char* file, AST* node_at, const char* msg, ...) {
va_list ap;
va_start(ap, msg);
char buf[1024];
vsnprintf(buf, sizeof(buf), msg, ap);
// TODO I'm not sure that it's safe to raise an exception here, since I think
// there will be things that end up not getting cleaned up.
// Then again, there are a huge number of things that don't get cleaned up even
// if an exception doesn't get thrown...
// TODO output is still a little wrong, should be, for example
//
// File "../test/tests/future_non_existent.py", line 1
// from __future__ import rvalue_references # should cause syntax error
//
// but instead it is
//
// Traceback (most recent call last):
// File "../test/tests/future_non_existent.py", line -1, in :
// from __future__ import rvalue_references # should cause syntax error
::pyston::raiseSyntaxError(buf, node_at->lineno, node_at->col_offset, file, "");
}
void raiseFutureImportErrorNotFound(const char* file, AST* node, const char* name) {
raiseSyntaxError(file, node, "future feature %s is not defined", name);
raiseSyntaxErrorHelper(file, "", node, "future feature %s is not defined", name);
}
void raiseFutureImportErrorNotBeginning(const char* file, AST* node) {
raiseSyntaxError(file, node, "from __future__ imports must occur at the beginning of the file");
raiseSyntaxErrorHelper(file, "", node, "from __future__ imports must occur at the beginning of the file");
}
class BadFutureImportVisitor : public NoopASTVisitor {
......
......@@ -30,6 +30,7 @@
#include "codegen/parser.h"
#include "codegen/patchpoints.h"
#include "codegen/stackmaps.h"
#include "codegen/unwinding.h"
#include "core/ast.h"
#include "core/cfg.h"
#include "core/common.h"
......@@ -44,7 +45,8 @@ namespace pyston {
// TODO terrible place for these!
ParamNames::ParamNames(AST* ast) : takes_param_names(true) {
if (ast->type == AST_TYPE::Module || ast->type == AST_TYPE::ClassDef || ast->type == AST_TYPE::Expression) {
if (ast->type == AST_TYPE::Module || ast->type == AST_TYPE::ClassDef || ast->type == AST_TYPE::Expression
|| ast->type == AST_TYPE::Suite) {
kwarg = "";
vararg = "";
} else if (ast->type == AST_TYPE::FunctionDef || ast->type == AST_TYPE::Lambda) {
......@@ -95,6 +97,7 @@ const std::string SourceInfo::getName() {
return "<lambda>";
case AST_TYPE::Module:
case AST_TYPE::Expression:
case AST_TYPE::Suite:
return "<module>";
default:
RELEASE_ASSERT(0, "%d", ast->type);
......@@ -304,7 +307,7 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
bm->future_flags = getFutureFlags(m, bm->fn.c_str());
ScopingAnalysis* scoping = runScopingAnalysis(m);
ScopingAnalysis* scoping = new ScopingAnalysis(m);
SourceInfo* si = new SourceInfo(bm, scoping, m, m->body);
CLFunction* cl_f = new CLFunction(0, 0, false, false, si);
......@@ -323,40 +326,71 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
((void (*)())cf->code)();
}
static Box* compileAndRunExpression(AST_Expression* expr, BoxedModule* bm, BoxedDict* locals) {
template <typename AST_Type>
Box* evalOrExec(AST_Type* source, std::vector<AST_stmt*>& body, BoxedModule* bm, Box* boxedLocals) {
CompiledFunction* cf;
{ // scope for limiting the locked region:
LOCK_REGION(codegen_rwlock.asWrite());
Timer _t("for compileEval()");
Timer _t("for evalOrExec()");
ScopingAnalysis* scoping = new ScopingAnalysis(expr);
ScopingAnalysis* scoping = new ScopingAnalysis(source);
AST_Return* stmt = new AST_Return();
stmt->value = expr->body;
SourceInfo* si = new SourceInfo(bm, scoping, expr, { stmt });
SourceInfo* si = new SourceInfo(bm, scoping, source, body);
CLFunction* cl_f = new CLFunction(0, 0, false, false, si);
// TODO Right now we only support going into an exec or eval through the
// intepretter, since the interpretter has a special function which lets
// us set the locals object. We should probably support it for optimized
// code as well, so we could use initialEffort() here instead of hard-coding
// INTERPRETED. This could actually be useful if we actually cache the parse
// results (since sometimes eval or exec might be called on constant strings).
EffortLevel effort = EffortLevel::INTERPRETED;
cf = compileFunction(cl_f, new FunctionSpecialization(VOID), effort, NULL);
assert(cf->clfunc->versions.size());
}
return astInterpretFunctionEval(cf, locals);
return astInterpretFunctionEval(cf, boxedLocals);
}
Box* runEval(const char* code, BoxedDict* locals, BoxedModule* module) {
// Main entrypoints for eval and exec.
Box* eval(Box* boxedCode) {
Box* boxedLocals = fastLocalsToBoxedLocals();
BoxedModule* module = getCurrentModule();
// TODO error message if parse fails or if it isn't an expr
// TODO should have a cleaner interface that can parse the Expression directly
// TODO this memory leaks
RELEASE_ASSERT(boxedCode->cls == str_cls, "");
const char* code = static_cast<BoxedString*>(boxedCode)->s.c_str();
AST_Module* parsedModule = parse_string(code);
assert(parsedModule->body[0]->type == AST_TYPE::Expr);
RELEASE_ASSERT(parsedModule->body[0]->type == AST_TYPE::Expr, "");
AST_Expression* parsedExpr = new AST_Expression(std::move(parsedModule->interned_strings));
parsedExpr->body = static_cast<AST_Expr*>(parsedModule->body[0])->value;
return compileAndRunExpression(parsedExpr, module, locals);
// We need body (list of statements) to compile.
// Obtain this by simply making a single statement which contains the expression.
AST_Return* stmt = new AST_Return();
stmt->value = parsedExpr->body;
std::vector<AST_stmt*> body = { stmt };
return evalOrExec<AST_Expression>(parsedExpr, body, module, boxedLocals);
}
Box* exec(Box* boxedCode) {
Box* boxedLocals = fastLocalsToBoxedLocals();
BoxedModule* module = getCurrentModule();
// TODO same issues as in `eval`
RELEASE_ASSERT(boxedCode->cls == str_cls, "");
const char* code = static_cast<BoxedString*>(boxedCode)->s.c_str();
AST_Module* parsedModule = parse_string(code);
AST_Suite* parsedSuite = new AST_Suite(std::move(parsedModule->interned_strings));
parsedSuite->body = parsedModule->body;
return evalOrExec<AST_Suite>(parsedSuite, parsedSuite->body, module, boxedLocals);
}
// If a function version keeps failing its speculations, kill it (remove it
......@@ -456,9 +490,8 @@ CompiledFunction* compilePartialFuncInternal(OSRExit* exit) {
assert(exit->parent_cf->clfunc);
CompiledFunction*& new_cf = exit->parent_cf->clfunc->osr_versions[exit->entry];
if (new_cf == NULL) {
EffortLevel new_effort = EffortLevel::MAXIMAL;
if (exit->parent_cf->effort == EffortLevel::INTERPRETED)
new_effort = EffortLevel::MINIMAL;
EffortLevel new_effort = exit->parent_cf->effort == EffortLevel::INTERPRETED ? EffortLevel::MINIMAL
: EffortLevel::MAXIMAL;
CompiledFunction* compiled = compileFunction(exit->parent_cf->clfunc, NULL, new_effort, exit->entry);
assert(compiled == new_cf);
......
......@@ -37,7 +37,8 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm);
// will we always want to generate unique function names? (ie will this function always be reasonable?)
CompiledFunction* cfForMachineFunctionName(const std::string&);
Box* runEval(const char* code, BoxedDict* locals, BoxedModule* module);
extern "C" Box* exec(Box* boxedCode);
extern "C" Box* eval(Box* boxedCode);
}
#endif
......@@ -72,8 +72,39 @@ llvm::Value* IRGenState::getScratchSpace(int min_bytes) {
return scratch_space;
}
static llvm::Value* getBoxedLocalsGep(llvm::IRBuilder<true>& builder, llvm::Value* v) {
static_assert(offsetof(FrameInfo, exc) == 0, "");
static_assert(sizeof(ExcInfo) == 24, "");
static_assert(offsetof(FrameInfo, boxedLocals) == 24, "");
return builder.CreateConstInBoundsGEP2_32(v, 0, 1);
}
static llvm::Value* getExcinfoGep(llvm::IRBuilder<true>& builder, llvm::Value* v) {
static_assert(offsetof(FrameInfo, exc) == 0, "");
static_assert(offsetof(ExcInfo, type) == 0, "");
return builder.CreateConstInBoundsGEP2_32(builder.CreateConstInBoundsGEP2_32(v, 0, 0), 0, 0);
}
llvm::Value* IRGenState::getFrameInfoVar() {
if (!frame_info) {
/*
There is a matrix of possibilities here.
For complete (non-OSR) functions, we initialize the FrameInfo* with an alloca and
set this->frame_info to that llvm value.
- If the function has NAME-scope, we initialize the frame_info.boxedLocals to a dictionary
and set this->boxed_locals to an llvm value which is that dictionary.
- If it is non-NAME-scope, we leave it NULL, because most of the time it won't
be initialized (unless someone calls locals() or something).
this->boxed_locals is unused within the IR, so we don't set it.
If this is an OSR function, then a FrameInfo* is passed in as an argument, so we don't
need to initialize it with an alloca, and frame_info is already
pointer.
- If the function is NAME-scope, we extract the boxedLocals from the frame_info in order
to set this->boxed_locals.
*/
if (!this->frame_info) {
llvm::BasicBlock& entry_block = getLLVMFunction()->getEntryBlock();
llvm::IRBuilder<true> builder(&entry_block);
......@@ -82,18 +113,52 @@ llvm::Value* IRGenState::getFrameInfoVar() {
builder.SetInsertPoint(&entry_block, entry_block.getFirstInsertionPt());
llvm::AllocaInst* al = builder.CreateAlloca(g.frame_info_type, NULL, "frame_info");
llvm::AllocaInst* al = builder.CreateAlloca(g.llvm_frame_info_type, NULL, "frame_info");
assert(al->isStaticAlloca());
static_assert(offsetof(FrameInfo, exc) == 0, "");
static_assert(offsetof(ExcInfo, type) == 0, "");
llvm::Value* exctype_gep
= builder.CreateConstInBoundsGEP2_32(builder.CreateConstInBoundsGEP2_32(al, 0, 0), 0, 0);
builder.CreateStore(embedConstantPtr(NULL, g.llvm_value_type_ptr), exctype_gep);
if (entry_block.getTerminator())
builder.SetInsertPoint(entry_block.getTerminator());
else
builder.SetInsertPoint(&entry_block);
if (frame_info_arg) {
// The OSR case
this->frame_info = frame_info_arg;
if (getScopeInfo()->usesNameLookup()) {
// load frame_info.boxedLocals
this->boxed_locals = builder.CreateLoad(getBoxedLocalsGep(builder, this->frame_info));
}
} else {
// The "normal" case
// frame_info.exc.type = NULL
builder.CreateStore(embedConstantPtr(NULL, g.llvm_value_type_ptr), getExcinfoGep(builder, al));
// frame_info.boxedLocals = NULL
llvm::Value* boxed_locals_gep = getBoxedLocalsGep(builder, al);
builder.CreateStore(embedConstantPtr(NULL, g.llvm_value_type_ptr), boxed_locals_gep);
frame_info = al;
if (getScopeInfo()->usesNameLookup()) {
// frame_info.boxedLocals = createDict()
// (Since this can call into the GC, we have to initialize it to NULL first as we did above.)
this->boxed_locals = builder.CreateCall(g.funcs.createDict);
builder.CreateStore(this->boxed_locals, boxed_locals_gep);
}
return frame_info;
this->frame_info = al;
}
}
return this->frame_info;
}
llvm::Value* IRGenState::getBoxedLocalsVar() {
assert(getScopeInfo()->usesNameLookup());
getFrameInfoVar(); // ensures this->boxed_locals_var is initialized
assert(this->boxed_locals != NULL);
return this->boxed_locals;
}
ScopeInfo* IRGenState::getScopeInfo() {
......@@ -275,6 +340,7 @@ static std::vector<const std::string*>* getKeywordNameStorage(AST_Call* node) {
const std::string CREATED_CLOSURE_NAME = "#created_closure";
const std::string PASSED_CLOSURE_NAME = "#passed_closure";
const std::string PASSED_GENERATOR_NAME = "#passed_generator";
const std::string FRAME_INFO_PTR_NAME = "#frame_info_ptr";
bool isIsDefinedName(const std::string& name) {
return startswith(name, "!is_defined_");
......@@ -444,53 +510,7 @@ private:
}
}
case AST_LangPrimitive::LOCALS: {
assert(node->args.size() == 0);
llvm::Value* v = emitter.getBuilder()->CreateCall(g.funcs.createDict);
ConcreteCompilerVariable* rtn = new ConcreteCompilerVariable(DICT, v, true);
for (auto& p : symbol_table) {
if (p.first.str()[0] == '!' || p.first.str()[0] == '#')
continue;
ConcreteCompilerVariable* is_defined_var
= static_cast<ConcreteCompilerVariable*>(_getFake(getIsDefinedName(p.first), true));
static const std::string setitem_str("__setitem__");
if (!is_defined_var) {
ConcreteCompilerVariable* converted = p.second->makeConverted(emitter, p.second->getBoxType());
// TODO super dumb that it reallocates the name again
CallattrFlags flags = {.cls_only = true, .null_on_nonexistent = false };
CompilerVariable* _r
= rtn->callattr(emitter, getEmptyOpInfo(unw_info), &setitem_str, flags, ArgPassSpec(2),
{ makeStr(new std::string(p.first.str())), converted }, NULL);
converted->decvref(emitter);
_r->decvref(emitter);
} else {
assert(is_defined_var->getType() == BOOL);
llvm::BasicBlock* was_defined
= llvm::BasicBlock::Create(g.context, "was_defined", irstate->getLLVMFunction());
llvm::BasicBlock* join
= llvm::BasicBlock::Create(g.context, "join", irstate->getLLVMFunction());
emitter.getBuilder()->CreateCondBr(i1FromBool(emitter, is_defined_var), was_defined, join);
emitter.getBuilder()->SetInsertPoint(was_defined);
ConcreteCompilerVariable* converted = p.second->makeConverted(emitter, p.second->getBoxType());
// TODO super dumb that it reallocates the name again
CallattrFlags flags = {.cls_only = true, .null_on_nonexistent = false };
CompilerVariable* _r
= rtn->callattr(emitter, getEmptyOpInfo(unw_info), &setitem_str, flags, ArgPassSpec(2),
{ makeStr(new std::string(p.first.str())), converted }, NULL);
converted->decvref(emitter);
_r->decvref(emitter);
emitter.getBuilder()->CreateBr(join);
emitter.getBuilder()->SetInsertPoint(join);
}
}
return rtn;
return new ConcreteCompilerVariable(UNKNOWN, irstate->getBoxedLocalsVar(), true);
}
case AST_LangPrimitive::GET_ITER: {
assert(node->args.size() == 1);
......@@ -882,14 +902,15 @@ private:
assert(closure);
return closure->getattr(emitter, getEmptyOpInfo(unw_info), &node->id.str(), false);
} else if (vst == ScopeInfo::VarScopeType::NAME) {
llvm::Value* boxedLocals = irstate->getBoxedLocalsVar();
llvm::Value* attr = getStringConstantPtr(node->id.str() + '\0');
llvm::Value* module = embedConstantPtr(irstate->getSourceInfo()->parent_module, g.llvm_module_type_ptr);
llvm::Value* r = emitter.createCall3(unw_info, g.funcs.boxedLocalsGet, boxedLocals, attr, module);
return new ConcreteCompilerVariable(UNKNOWN, r, true);
} else {
// vst is one of {FAST, CLOSURE, NAME}
if (symbol_table.find(node->id) == symbol_table.end()) {
// classdefs have different scoping rules than functions:
if (vst == ScopeInfo::VarScopeType::NAME) {
return _getGlobal(node, unw_info);
}
// TODO should mark as DEAD here, though we won't end up setting all the names appropriately
// state = DEAD;
llvm::CallSite call = emitter.createCall(
......@@ -905,18 +926,6 @@ private:
= static_cast<ConcreteCompilerVariable*>(_getFake(defined_name, true));
if (is_defined_var) {
if (vst == ScopeInfo::VarScopeType::NAME) {
llvm::Value* v = handlePotentiallyUndefined(
is_defined_var, g.llvm_value_type_ptr, curblock, emitter, false,
[=](IREmitter& emitter) {
CompilerVariable* local = symbol_table[node->id];
return local->makeConverted(emitter, local->getBoxType())->getValue();
},
[=](IREmitter& emitter) { return _getGlobal(node, unw_info)->getValue(); });
return new ConcreteCompilerVariable(UNKNOWN, v, true);
}
emitter.createCall(unw_info, g.funcs.assertNameDefined,
{ i1FromBool(emitter, is_defined_var), getStringConstantPtr(node->id.str() + '\0'),
embedConstantPtr(UnboundLocalError, g.llvm_class_type_ptr),
......@@ -1388,19 +1397,27 @@ private:
// only updates symbol_table if we're *not* setting a global
void _doSet(InternedString name, CompilerVariable* val, UnwindInfo unw_info) {
assert(name.str() != "None");
assert(name.str() != FRAME_INFO_PTR_NAME);
auto scope_info = irstate->getScopeInfo();
assert(!scope_info->refersToClosure(name));
if (scope_info->refersToGlobal(name)) {
assert(!scope_info->saveInClosure(name));
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(name);
assert(vst != ScopeInfo::VarScopeType::DEREF);
if (vst == ScopeInfo::VarScopeType::GLOBAL) {
// TODO do something special here so that it knows to only emit a monomorphic inline cache?
ConcreteCompilerVariable* module = new ConcreteCompilerVariable(
MODULE, embedConstantPtr(irstate->getSourceInfo()->parent_module, g.llvm_value_type_ptr), false);
module->setattr(emitter, getEmptyOpInfo(unw_info), &name.str(), val);
module->decvref(emitter);
} else if (vst == ScopeInfo::VarScopeType::NAME) {
// TODO inefficient
llvm::Value* boxedLocals = irstate->getBoxedLocalsVar();
llvm::Value* attr = getStringConstantPtr(name.str() + '\0');
emitter.createCall3(unw_info, g.funcs.boxedLocalsSet, boxedLocals, attr,
val->makeConverted(emitter, UNKNOWN)->getValue());
} else {
// FAST or CLOSURE
CompilerVariable*& prev = symbol_table[name];
if (prev != NULL) {
prev->decvref(emitter);
......@@ -1413,7 +1430,7 @@ private:
InternedString defined_name = getIsDefinedName(name);
_popFake(defined_name, true);
if (scope_info->saveInClosure(name)) {
if (vst == ScopeInfo::VarScopeType::CLOSURE) {
CompilerVariable* closure = symbol_table[internString(CREATED_CLOSURE_NAME)];
assert(closure);
......@@ -1586,7 +1603,8 @@ private:
void _doDelName(AST_Name* target, UnwindInfo unw_info) {
auto scope_info = irstate->getScopeInfo();
if (scope_info->refersToGlobal(target->id)) {
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(target->id);
if (vst == ScopeInfo::VarScopeType::GLOBAL) {
// Can't use delattr since the errors are different:
emitter.createCall2(unw_info, g.funcs.delGlobal,
embedConstantPtr(irstate->getSourceInfo()->parent_module, g.llvm_module_type_ptr),
......@@ -1594,18 +1612,22 @@ private:
return;
}
assert(!scope_info->refersToClosure(target->id));
assert(!scope_info->saveInClosure(
target->id)); // SyntaxError: can not delete variable 'x' referenced in nested scope
if (vst == ScopeInfo::VarScopeType::NAME) {
llvm::Value* boxedLocals = irstate->getBoxedLocalsVar();
llvm::Value* attr = getStringConstantPtr(target->id.str() + '\0');
emitter.createCall2(unw_info, g.funcs.boxedLocalsDel, boxedLocals, attr);
return;
}
// A del of a missing name generates different error messages in a function scope vs a classdef scope
bool local_error_msg = (irstate->getSourceInfo()->ast->type != AST_TYPE::ClassDef);
// Can't be in a closure because of this syntax error:
// SyntaxError: can not delete variable 'x' referenced in nested scope
assert(vst == ScopeInfo::VarScopeType::FAST);
if (symbol_table.count(target->id) == 0) {
llvm::CallSite call = emitter.createCall(
unw_info, g.funcs.assertNameDefined,
{ getConstantInt(0, g.i1), getStringConstantPtr(target->id.str() + '\0'),
embedConstantPtr(NameError, g.llvm_class_type_ptr), getConstantInt(local_error_msg, g.i1) });
embedConstantPtr(NameError, g.llvm_class_type_ptr), getConstantInt(true /*local_error_msg*/, g.i1) });
call.setDoesNotReturn();
return;
}
......@@ -1617,13 +1639,25 @@ private:
emitter.createCall(unw_info, g.funcs.assertNameDefined,
{ i1FromBool(emitter, is_defined_var), getStringConstantPtr(target->id.str() + '\0'),
embedConstantPtr(NameError, g.llvm_class_type_ptr),
getConstantInt(local_error_msg, g.i1) });
getConstantInt(true /*local_error_msg*/, g.i1) });
_popFake(defined_name);
}
symbol_table.erase(target->id);
}
void doExec(AST_Exec* node, UnwindInfo unw_info) {
// TODO locals and globals
RELEASE_ASSERT(!node->globals, "do not support exec with globals or locals yet");
assert(!node->locals);
CompilerVariable* body = evalExpr(node->body, unw_info);
ConcreteCompilerVariable* cbody = body->makeConverted(emitter, body->getBoxType());
body->decvref(emitter);
emitter.createCall(unw_info, g.funcs.exec, cbody->getValue());
}
void doPrint(AST_Print* node, UnwindInfo unw_info) {
ConcreteCompilerVariable* dest = NULL;
if (node->dest) {
......@@ -1808,6 +1842,9 @@ private:
SortedSymbolTable sorted_symbol_table(symbol_table.begin(), symbol_table.end());
sorted_symbol_table[internString(FRAME_INFO_PTR_NAME)]
= new ConcreteCompilerVariable(FRAME_INFO, irstate->getFrameInfoVar(), true);
// For OSR calls, we use the same calling convention as in some other places; namely,
// arg1, arg2, arg3, argarray [nargs is ommitted]
// It would be nice to directly pass all variables as arguments, instead of packing them into
......@@ -1884,6 +1921,9 @@ private:
val = emitter.getBuilder()->CreateIntToPtr(val, g.llvm_value_type_ptr);
} else if (var->getType() == CLOSURE) {
ptr = emitter.getBuilder()->CreateBitCast(ptr, g.llvm_closure_type_ptr->getPointerTo());
} else if (var->getType() == FRAME_INFO) {
ptr = emitter.getBuilder()->CreateBitCast(ptr,
g.llvm_frame_info_type->getPointerTo()->getPointerTo());
} else {
assert(val->getType() == g.llvm_value_type_ptr);
}
......@@ -1995,6 +2035,9 @@ private:
case AST_TYPE::Delete:
doDelete(ast_cast<AST_Delete>(node), unw_info);
break;
case AST_TYPE::Exec:
doExec(ast_cast<AST_Exec>(node), unw_info);
break;
case AST_TYPE::Expr:
doExpr(ast_cast<AST_Expr>(node), unw_info);
break;
......@@ -2050,6 +2093,7 @@ private:
}
void loadArgument(InternedString name, ConcreteCompilerType* t, llvm::Value* v, UnwindInfo unw_info) {
assert(name.str() != FRAME_INFO_PTR_NAME);
ConcreteCompilerVariable* var = unboxVar(t, v, false);
_doSet(name, var, unw_info);
var->decvref(emitter);
......@@ -2078,6 +2122,7 @@ private:
// Additional names to remove; remove them after iteration is done to not mess up the iterators
std::vector<InternedString> also_remove;
for (auto it = symbol_table.begin(); it != symbol_table.end();) {
assert(it->first.str() != FRAME_INFO_PTR_NAME);
if (allowableFakeEndingSymbol(it->first)) {
++it;
continue;
......@@ -2094,7 +2139,7 @@ private:
it->second->decvref(emitter);
it = symbol_table.erase(it);
} else if (source->phis->isRequiredAfter(it->first, myblock)) {
assert(!scope_info->refersToGlobal(it->first));
assert(scope_info->getScopeTypeOfName(it->first) != ScopeInfo::VarScopeType::GLOBAL);
ConcreteCompilerType* phi_type = types->getTypeAtBlockEnd(it->first, myblock);
// printf("Converting %s from %s to %s\n", it->first.c_str(),
// it->second->getType()->debugName().c_str(), phi_type->debugName().c_str());
......@@ -2127,7 +2172,7 @@ private:
const PhiAnalysis::RequiredSet& all_phis = source->phis->getAllRequiredAfter(myblock);
for (PhiAnalysis::RequiredSet::const_iterator it = all_phis.begin(), end = all_phis.end(); it != end; ++it) {
// printf("phi will be required for %s\n", it->c_str());
assert(!scope_info->refersToGlobal(*it));
assert(scope_info->getScopeTypeOfName(*it) != ScopeInfo::VarScopeType::GLOBAL);
CompilerVariable*& cur = symbol_table[*it];
InternedString defined_name = getIsDefinedName(*it);
......@@ -2240,6 +2285,8 @@ public:
ending_type = getCreatedClosureType();
} else if (it->first.str() == PASSED_GENERATOR_NAME) {
ending_type = GENERATOR;
} else if (it->first.str() == FRAME_INFO_PTR_NAME) {
ending_type = FRAME_INFO;
} else {
ending_type = types->getTypeAtBlockEnd(it->first, myblock);
}
......@@ -2256,7 +2303,9 @@ public:
void giveLocalSymbol(InternedString name, CompilerVariable* var) override {
assert(name.str() != "None");
ASSERT(!irstate->getScopeInfo()->refersToGlobal(name), "%s", name.c_str());
assert(name.str() != FRAME_INFO_PTR_NAME);
ASSERT(irstate->getScopeInfo()->getScopeTypeOfName(name) != ScopeInfo::VarScopeType::GLOBAL, "%s",
name.c_str());
assert(var->getType() != BOXED_INT);
assert(var->getType() != BOXED_FLOAT);
CompilerVariable*& cur = symbol_table[name];
......@@ -2313,7 +2362,6 @@ public:
++AI;
}
std::vector<llvm::Value*> python_parameters;
for (int i = 0; i < arg_types.size(); i++) {
assert(AI != irstate->getLLVMFunction()->arg_end());
......
......@@ -46,6 +46,7 @@ typedef std::unordered_map<InternedString, ConcreteCompilerVariable*> ConcreteSy
extern const std::string CREATED_CLOSURE_NAME;
extern const std::string PASSED_CLOSURE_NAME;
extern const std::string PASSED_GENERATOR_NAME;
extern const std::string FRAME_INFO_PTR_NAME;
// Class that holds state of the current IR generation, that might not be local
......@@ -61,13 +62,16 @@ private:
llvm::AllocaInst* scratch_space;
llvm::Value* frame_info;
llvm::Value* boxed_locals;
llvm::Value* frame_info_arg;
int scratch_size;
public:
IRGenState(CompiledFunction* cf, SourceInfo* source_info, ParamNames* param_names, GCBuilder* gc,
llvm::MDNode* func_dbg_info)
: cf(cf), source_info(source_info), param_names(param_names), gc(gc), func_dbg_info(func_dbg_info),
scratch_space(NULL), frame_info(NULL), scratch_size(0) {
scratch_space(NULL), frame_info(NULL), frame_info_arg(NULL), scratch_size(0) {
assert(cf->func);
assert(!cf->clfunc); // in this case don't need to pass in sourceinfo
}
......@@ -82,6 +86,7 @@ public:
llvm::Value* getScratchSpace(int min_bytes);
llvm::Value* getFrameInfoVar();
llvm::Value* getBoxedLocalsVar();
ConcreteCompilerType* getReturnType() { return cf->getReturnType(); }
......@@ -93,6 +98,8 @@ public:
llvm::MDNode* getFuncDbgInfo() { return func_dbg_info; }
ParamNames* getParamNames() { return param_names; }
void setFrameInfoArgument(llvm::Value* v) { frame_info_arg = v; }
};
// turns CFGBlocks into LLVM IR
......
......@@ -643,6 +643,7 @@ struct stmt_dispatcher {
ptr->locals = readItem(e.locals, interned_strings);
else
ptr->locals = NULL;
assert(ptr->globals || !ptr->locals);
return ptr;
}
......
......@@ -150,8 +150,8 @@ void initGlobalFuncs(GlobalState& g) {
g.llvm_excinfo_type = g.stdlib_module->getTypeByName("struct.pyston::ExcInfo");
assert(g.llvm_excinfo_type);
g.frame_info_type = g.stdlib_module->getTypeByName("struct.pyston::FrameInfo");
assert(g.frame_info_type);
g.llvm_frame_info_type = g.stdlib_module->getTypeByName("struct.pyston::FrameInfo");
assert(g.llvm_frame_info_type);
#define GET(N) g.funcs.N = getFunc((void*)N, STRINGIFY(N))
......@@ -222,6 +222,11 @@ void initGlobalFuncs(GlobalState& g) {
GET(listAppendInternal);
GET(getSysStdout);
GET(exec);
GET(boxedLocalsSet);
GET(boxedLocalsGet);
GET(boxedLocalsDel);
g.funcs.runtimeCall = getFunc((void*)runtimeCall, "runtimeCall");
g.funcs.runtimeCall0 = addFunc((void*)runtimeCall, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.i32);
g.funcs.runtimeCall1
......
......@@ -46,6 +46,8 @@ struct GlobalFuncs {
llvm::Value* runtimeCall0, *runtimeCall1, *runtimeCall2, *runtimeCall3, *runtimeCall, *runtimeCallN;
llvm::Value* callattr0, *callattr1, *callattr2, *callattr3, *callattr, *callattrN;
llvm::Value* reoptCompiledFunc, *compilePartialFunc;
llvm::Value* exec;
llvm::Value* boxedLocalsSet, *boxedLocalsGet, *boxedLocalsDel;
llvm::Value* __cxa_begin_catch, *__cxa_end_catch;
llvm::Value* raise0, *raise3;
......
......@@ -36,6 +36,7 @@
#include "codegen/stackmaps.h"
#include "core/util.h"
#include "runtime/ctxswitching.h"
#include "runtime/objmodel.h"
#include "runtime/generator.h"
#include "runtime/traceback.h"
#include "runtime/types.h"
......@@ -492,8 +493,8 @@ BoxedTraceback* getTraceback() {
Timer _t("getTraceback");
std::vector<const LineInfo*> entries;
for (auto& frame_info : unwindPythonFrames()) {
const LineInfo* line_info = lineInfoForFrame(frame_info);
for (auto& frame_iter : unwindPythonFrames()) {
const LineInfo* line_info = lineInfoForFrame(frame_iter);
if (line_info)
entries.push_back(line_info);
}
......@@ -557,16 +558,19 @@ BoxedModule* getCurrentModule() {
return compiledFunction->clfunc->source->parent_module;
}
BoxedDict* getLocals(bool only_user_visible, bool includeClosure) {
for (PythonFrameIterator& frame_info : unwindPythonFrames()) {
// TODO factor getStackLoclasIncludingUserHidden and fastLocalsToBoxedLocals
// because they are pretty ugly but have a pretty repetitive pattern.
FrameStackState getFrameStackState() {
for (PythonFrameIterator& frame_iter : unwindPythonFrames()) {
BoxedDict* d;
BoxedClosure* closure;
CompiledFunction* cf;
if (frame_info.getId().type == PythonFrameId::COMPILED) {
if (frame_iter.getId().type == PythonFrameId::COMPILED) {
d = new BoxedDict();
cf = frame_info.getCF();
uint64_t ip = frame_info.getId().ip;
cf = frame_iter.getCF();
uint64_t ip = frame_iter.getId().ip;
assert(ip > cf->code_start);
unsigned offset = ip - cf->code_start;
......@@ -587,7 +591,7 @@ BoxedDict* getLocals(bool only_user_visible, bool includeClosure) {
const auto& locs = e.locations;
assert(locs.size() == 1);
uint64_t v = frame_info.readLocation(locs[0]);
uint64_t v = frame_iter.readLocation(locs[0]);
if ((v & 1) == 0)
is_undefined.insert(p.first.substr(12));
......@@ -600,7 +604,81 @@ BoxedDict* getLocals(bool only_user_visible, bool includeClosure) {
if (p.first[0] == '!')
continue;
if (only_user_visible && p.first[0] == '#')
if (is_undefined.count(p.first))
continue;
for (const LocationMap::LocationTable::LocationEntry& e : p.second.locations) {
if (e.offset < offset && offset <= e.offset + e.length) {
const auto& locs = e.locations;
llvm::SmallVector<uint64_t, 1> vals;
// printf("%s: %s\n", p.first.c_str(), e.type->debugName().c_str());
for (auto& loc : locs) {
vals.push_back(frame_iter.readLocation(loc));
}
Box* v = e.type->deserializeFromFrame(vals);
// printf("%s: (pp id %ld) %p\n", p.first.c_str(), e._debug_pp_id, v);
assert(gc::isValidGCObject(v));
d->d[boxString(p.first)] = v;
}
}
}
} else {
abort();
}
return FrameStackState(d, frame_iter.getFrameInfo());
}
RELEASE_ASSERT(0, "Internal error: unable to find any python frames");
}
Box* fastLocalsToBoxedLocals() {
for (PythonFrameIterator& frame_iter : unwindPythonFrames()) {
BoxedDict* d;
BoxedClosure* closure;
CompiledFunction* cf;
FrameInfo* frame_info;
if (frame_iter.getId().type == PythonFrameId::COMPILED) {
d = new BoxedDict();
cf = frame_iter.getCF();
uint64_t ip = frame_iter.getId().ip;
assert(ip > cf->code_start);
unsigned offset = ip - cf->code_start;
assert(cf->location_map);
// We have to detect + ignore any entries for variables that
// could have been defined (so they have entries) but aren't (so the
// entries point to uninitialized memory).
std::unordered_set<std::string> is_undefined;
for (const auto& p : cf->location_map->names) {
if (!startswith(p.first, "!is_defined_"))
continue;
for (const LocationMap::LocationTable::LocationEntry& e : p.second.locations) {
if (e.offset < offset && offset <= e.offset + e.length) {
const auto& locs = e.locations;
assert(locs.size() == 1);
uint64_t v = frame_iter.readLocation(locs[0]);
if ((v & 1) == 0)
is_undefined.insert(p.first.substr(12));
break;
}
}
}
for (const auto& p : cf->location_map->names) {
if (p.first[0] == '!')
continue;
if (p.first[0] == '#')
continue;
if (is_undefined.count(p.first))
......@@ -614,7 +692,7 @@ BoxedDict* getLocals(bool only_user_visible, bool includeClosure) {
// printf("%s: %s\n", p.first.c_str(), e.type->debugName().c_str());
for (auto& loc : locs) {
vals.push_back(frame_info.readLocation(loc));
vals.push_back(frame_iter.readLocation(loc));
}
Box* v = e.type->deserializeFromFrame(vals);
......@@ -626,7 +704,7 @@ BoxedDict* getLocals(bool only_user_visible, bool includeClosure) {
}
closure = NULL;
if (includeClosure && cf->location_map->names.count(PASSED_CLOSURE_NAME) > 0) {
if (cf->location_map->names.count(PASSED_CLOSURE_NAME) > 0) {
for (const LocationMap::LocationTable::LocationEntry& e :
cf->location_map->names[PASSED_CLOSURE_NAME].locations) {
if (e.offset < offset && offset <= e.offset + e.length) {
......@@ -635,7 +713,7 @@ BoxedDict* getLocals(bool only_user_visible, bool includeClosure) {
llvm::SmallVector<uint64_t, 1> vals;
for (auto& loc : locs) {
vals.push_back(frame_info.readLocation(loc));
vals.push_back(frame_iter.readLocation(loc));
}
Box* v = e.type->deserializeFromFrame(vals);
......@@ -644,18 +722,25 @@ BoxedDict* getLocals(bool only_user_visible, bool includeClosure) {
}
}
}
} else if (frame_info.getId().type == PythonFrameId::INTERPRETED) {
d = localsForInterpretedFrame((void*)frame_info.getId().bp, only_user_visible);
if (includeClosure) {
closure = passedClosureForInterpretedFrame((void*)frame_info.getId().bp);
cf = getCFForInterpretedFrame((void*)frame_info.getId().bp);
}
frame_info = frame_iter.getFrameInfo();
} else if (frame_iter.getId().type == PythonFrameId::INTERPRETED) {
d = localsForInterpretedFrame((void*)frame_iter.getId().bp, true);
closure = passedClosureForInterpretedFrame((void*)frame_iter.getId().bp);
cf = getCFForInterpretedFrame((void*)frame_iter.getId().bp);
frame_info = getFrameInfoForInterpretedFrame((void*)frame_iter.getId().bp);
} else {
abort();
}
if (includeClosure) {
assert(frame_info);
if (frame_info->boxedLocals == NULL) {
frame_info->boxedLocals = new BoxedDict();
}
assert(gc::isValidGCObject(frame_info->boxedLocals));
// 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()) {
......@@ -663,7 +748,7 @@ BoxedDict* getLocals(bool only_user_visible, bool includeClosure) {
int offset = attr_offset.second;
Box* val = closure->attrs.attr_list->attrs[offset];
ScopeInfo* scope_info = cf->clfunc->source->getScopeInfo();
if (val != NULL && scope_info->refersToClosure(scope_info->internString(name))) {
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;
......@@ -671,9 +756,18 @@ BoxedDict* getLocals(bool only_user_visible, bool includeClosure) {
}
}
}
// Loop through all the values found above.
// TODO Right now d just has all the python variables that are *initialized*
// But we also need to loop through all the uninitialized variables that we have
// access to and delete them from the locals dict
for (const auto& p : d->d) {
Box* varname = p.first;
Box* value = p.second;
setitem(frame_info->boxedLocals, varname, value);
}
return d;
return frame_info->boxedLocals;
}
RELEASE_ASSERT(0, "Internal error: unable to find any python frames");
}
......
......@@ -21,14 +21,18 @@
namespace pyston {
class Box;
class BoxedDict;
class BoxedModule;
class BoxedTraceback;
struct FrameInfo;
BoxedModule* getCurrentModule();
class BoxedTraceback;
BoxedTraceback* getTraceback();
class BoxedDict;
BoxedDict* getLocals(bool only_user_visible, bool includeClosure);
// Adds stack locals and closure locals into the locals dict, and returns it.
Box* fastLocalsToBoxedLocals();
// Fetches a writeable pointer to the frame-local excinfo object,
// calculating it if necessary (from previous frames).
......@@ -39,6 +43,23 @@ struct ExecutionPoint {
AST_stmt* current_stmt;
};
ExecutionPoint getExecutionPoint();
struct FrameStackState {
// This includes all # variables (but not the ! ones).
// Therefore, it's not the same as the BoxedLocals.
// This also means that it contains
// CREATED_CLOSURE_NAME, PASSED_CLOSURE_NAME, and GENERATOR_NAME.
BoxedDict* locals;
// The frame_info is a pointer to the frame_info on the stack, so it is invalid
// after the frame ends.
FrameInfo* frame_info;
FrameStackState(BoxedDict* locals, FrameInfo* frame_info) : locals(locals), frame_info(frame_info) {}
};
// Returns all the stack locals, including hidden ones.
FrameStackState getFrameStackState();
}
#endif
......@@ -700,6 +700,14 @@ void AST_Expression::accept(ASTVisitor* v) {
body->accept(v);
}
void AST_Suite::accept(ASTVisitor* v) {
bool skip = v->visit_suite(this);
if (skip)
return;
visitVector(body, v);
}
void AST_Name::accept(ASTVisitor* v) {
bool skip = v->visit_name(this);
}
......@@ -1561,6 +1569,15 @@ bool PrintVisitor::visit_expression(AST_Expression* node) {
return true;
}
bool PrintVisitor::visit_suite(AST_Suite* node) {
for (int i = 0; i < node->body.size(); i++) {
printIndent();
node->body[i]->accept(this);
printf("\n");
}
return true;
}
bool PrintVisitor::visit_name(AST_Name* node) {
printf("%s", node->id.c_str());
// printf("%s(%d)", node->id.c_str(), node->ctx_type);
......
......@@ -23,6 +23,7 @@
#include "llvm/ADT/StringRef.h"
#include "analysis/scoping_analysis.h"
#include "core/common.h"
#include "core/stringpool.h"
......@@ -120,6 +121,7 @@ enum AST_TYPE {
Ellipsis = 87,
Expression = 88, // like Module, but used for eval.
SetComp = 89,
Suite = 90,
// Pseudo-nodes that are specific to this compiler:
Branch = 200,
......@@ -676,6 +678,20 @@ public:
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Module;
};
class AST_Suite : public AST {
public:
std::unique_ptr<InternedStringPool> interned_strings;
std::vector<AST_stmt*> body;
virtual void accept(ASTVisitor* v);
AST_Suite(std::unique_ptr<InternedStringPool> interned_strings)
: AST(AST_TYPE::Suite), interned_strings(std::move(interned_strings)) {}
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Suite;
};
class AST_Name : public AST_expr {
public:
AST_TYPE::AST_TYPE ctx_type;
......@@ -684,20 +700,14 @@ public:
// The resolved scope of this name. Kind of hacky to be storing it in the AST node;
// in CPython it ends up getting "cached" by being translated into one of a number of
// different bytecodes.
// We don't have a separate bytecode representation, so just store it in here for now.
enum LookupType {
UNKNOWN,
GLOBAL,
CLOSURE,
FAST_LOCAL,
LOCAL,
} lookup_type;
ScopeInfo::VarScopeType lookup_type;
virtual void accept(ASTVisitor* v);
virtual void* accept_expr(ExprVisitor* v);
AST_Name(InternedString id, AST_TYPE::AST_TYPE ctx_type, int lineno, int col_offset = 0)
: AST_expr(AST_TYPE::Name, lineno, col_offset), ctx_type(ctx_type), id(id), lookup_type(UNKNOWN) {}
: AST_expr(AST_TYPE::Name, lineno, col_offset), ctx_type(ctx_type), id(id),
lookup_type(ScopeInfo::VarScopeType::UNKNOWN) {}
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Name;
};
......@@ -1105,6 +1115,7 @@ public:
virtual bool visit_exec(AST_Exec* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_expr(AST_Expr* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_expression(AST_Expression* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_suite(AST_Suite* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_extslice(AST_ExtSlice* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_for(AST_For* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_functiondef(AST_FunctionDef* node) { RELEASE_ASSERT(0, ""); }
......@@ -1177,6 +1188,7 @@ public:
virtual bool visit_exec(AST_Exec* node) { return false; }
virtual bool visit_expr(AST_Expr* node) { return false; }
virtual bool visit_expr(AST_Expression* node) { return false; }
virtual bool visit_suite(AST_Suite* node) { return false; }
virtual bool visit_extslice(AST_ExtSlice* node) { return false; }
virtual bool visit_for(AST_For* node) { return false; }
virtual bool visit_functiondef(AST_FunctionDef* node) { return false; }
......@@ -1326,6 +1338,7 @@ public:
virtual bool visit_exec(AST_Exec* node);
virtual bool visit_expr(AST_Expr* node);
virtual bool visit_expression(AST_Expression* node);
virtual bool visit_suite(AST_Suite* node);
virtual bool visit_extslice(AST_ExtSlice* node);
virtual bool visit_for(AST_For* node);
virtual bool visit_functiondef(AST_FunctionDef* node);
......
......@@ -1913,7 +1913,16 @@ public:
return true;
}
bool visit_exec(AST_Exec* node) override { raiseExcHelper(SyntaxError, "'exec' currently not supported"); }
bool visit_exec(AST_Exec* node) override {
AST_Exec* astexec = new AST_Exec();
astexec->lineno = node->lineno;
astexec->col_offset = node->col_offset;
astexec->body = remapExpr(node->body);
astexec->globals = remapExpr(node->globals);
astexec->locals = remapExpr(node->locals);
push_back(astexec);
return true;
}
bool visit_while(AST_While* node) override {
assert(curblock);
......
......@@ -110,7 +110,8 @@ typedef ValuedCompilerType<llvm::Value*> ConcreteCompilerType;
ConcreteCompilerType* typeFromClass(BoxedClass*);
extern ConcreteCompilerType* INT, *BOXED_INT, *LONG, *FLOAT, *BOXED_FLOAT, *VOID, *UNKNOWN, *BOOL, *STR, *NONE, *LIST,
*SLICE, *MODULE, *DICT, *BOOL, *BOXED_BOOL, *BOXED_TUPLE, *SET, *FROZENSET, *CLOSURE, *GENERATOR, *BOXED_COMPLEX;
*SLICE, *MODULE, *DICT, *BOOL, *BOXED_BOOL, *BOXED_TUPLE, *SET, *FROZENSET, *CLOSURE, *GENERATOR, *BOXED_COMPLEX,
*FRAME_INFO;
extern CompilerType* UNDEF;
class CompilerVariable;
......@@ -535,6 +536,7 @@ void addToSysArgv(const char* str);
// The traceback given to the user will include this,
// even though the execution didn't actually arrive there.
void raiseSyntaxError(const char* msg, int lineno, int col_offset, const std::string& file, const std::string& func);
void raiseSyntaxErrorHelper(const std::string& file, const std::string& func, AST* node_at, const char* msg, ...);
struct LineInfo {
public:
......@@ -564,7 +566,9 @@ struct FrameInfo {
// - This makes frame entering+leaving faster at the expense of slower exceptions.
ExcInfo exc;
FrameInfo(ExcInfo exc) : exc(exc) {}
Box* boxedLocals;
FrameInfo(ExcInfo exc) : exc(exc), boxedLocals(NULL) {}
};
struct CallattrFlags {
......
......@@ -673,16 +673,6 @@ Box* zip2(Box* container1, Box* container2) {
return rtn;
}
Box* eval(Box* code) {
// TODO implement full functionality (args and stuff)
RELEASE_ASSERT(code->cls == str_cls, "eval not implemented for non-strings");
BoxedDict* locals = getLocals(true /* only_user_visible */, true /* includeClosure */);
BoxedModule* module = getCurrentModule();
return runEval(static_cast<BoxedString*>(code)->s.c_str(), locals, module);
}
static Box* callable(Box* obj) {
Box* r = PyBool_FromLong((long)PyCallable_Check(obj));
checkAndThrowCAPIException();
......@@ -849,7 +839,7 @@ Box* globals() {
}
Box* locals() {
return getLocals(true /* filter */, true /* includeClosure */);
return fastLocalsToBoxedLocals();
}
Box* divmod(Box* lhs, Box* rhs) {
......
......@@ -15,6 +15,7 @@
// This file is for forcing the inclusion of function declarations into the stdlib.
// This is so that the types of the functions are available to the compiler.
#include "codegen/irgen/hooks.h"
#include "core/types.h"
#include "gc/heap.h"
#include "runtime/complex.h"
......@@ -119,6 +120,8 @@ void force() {
FORCE(mod_float_float);
FORCE(pow_float_float);
FORCE(exec);
FORCE(dump);
FORCE(boxFloat);
......@@ -127,6 +130,10 @@ void force() {
FORCE(gc::sizes);
FORCE(boxedLocalsSet);
FORCE(boxedLocalsGet);
FORCE(boxedLocalsDel);
// FORCE(listIter);
}
}
......
......@@ -162,13 +162,13 @@ extern "C" Box* deopt(AST_expr* expr, Box* value) {
static StatCounter num_deopt("num_deopt");
num_deopt.log();
auto locals = getLocals(false /* filter */, false /* includeClosure */);
FrameStackState frame_state = getFrameStackState();
auto execution_point = getExecutionPoint();
// Should we only do this selectively?
execution_point.cf->speculationFailed();
return astInterpretFrom(execution_point.cf, expr, execution_point.current_stmt, value, locals);
return astInterpretFrom(execution_point.cf, expr, execution_point.current_stmt, value, frame_state);
}
extern "C" bool softspace(Box* b, bool newval) {
......@@ -4440,4 +4440,36 @@ Box* coerceUnicodeToStr(Box* unicode) {
return r;
}
extern "C" void boxedLocalsSet(Box* boxedLocals, const char* attr, Box* val) {
setitem(boxedLocals, boxString(attr), val);
}
extern "C" Box* boxedLocalsGet(Box* boxedLocals, const char* attr, BoxedModule* parent_module) {
assert(parent_module->cls == module_cls);
assert(boxedLocals != NULL);
RELEASE_ASSERT(boxedLocals->cls == dict_cls, "we don't support non-dict here yet");
auto& d = static_cast<BoxedDict*>(boxedLocals)->d;
auto it = d.find(boxString(attr));
if (it != d.end()) {
Box* value = it->second;
return value;
}
// TODO exception name?
std::string attr_string(attr);
return getGlobal(parent_module, &attr_string);
}
extern "C" void boxedLocalsDel(Box* boxedLocals, const char* attr) {
assert(boxedLocals != NULL);
RELEASE_ASSERT(boxedLocals->cls == dict_cls, "we don't support non-dict here yet");
auto& d = static_cast<BoxedDict*>(boxedLocals)->d;
auto it = d.find(boxString(attr));
if (it == d.end()) {
assertNameDefined(0, attr, NameError, false /* local_var_msg */);
}
d.erase(it);
}
}
......@@ -167,5 +167,9 @@ inline std::tuple<Box*, Box*, Box*, Box**> getTupleFromArgsArray(Box** args, int
Box** argtuple = num_args >= 4 ? &args[3] : nullptr;
return std::make_tuple(arg1, arg2, arg3, argtuple);
}
extern "C" void boxedLocalsSet(Box* boxedLocals, const char* attr, Box* val);
extern "C" Box* boxedLocalsGet(Box* boxedLocals, const char* attr, BoxedModule* parent_module);
extern "C" void boxedLocalsDel(Box* boxedLocals, const char* attr);
}
#endif
......@@ -123,6 +123,32 @@ void raiseSyntaxError(const char* msg, int lineno, int col_offset, const std::st
raiseRaw(ExcInfo(exc->cls, exc, new BoxedTraceback(std::move(entries))));
}
void raiseSyntaxErrorHelper(const std::string& file, const std::string& func, AST* node_at, const char* msg, ...) {
va_list ap;
va_start(ap, msg);
char buf[1024];
vsnprintf(buf, sizeof(buf), msg, ap);
// TODO I'm not sure that it's safe to raise an exception here, since I think
// there will be things that end up not getting cleaned up.
// Then again, there are a huge number of things that don't get cleaned up even
// if an exception doesn't get thrown...
// TODO output is still a little wrong, should be, for example
//
// File "../test/tests/future_non_existent.py", line 1
// from __future__ import rvalue_references # should cause syntax error
//
// but instead it is
//
// Traceback (most recent call last):
// File "../test/tests/future_non_existent.py", line -1, in :
// from __future__ import rvalue_references # should cause syntax error
raiseSyntaxError(buf, node_at->lineno, node_at->col_offset, file, "");
}
void _printStacktrace() {
printTraceback(getTraceback());
}
......
# skip-if: '-O' in EXTRA_JIT_ARGS
# statcheck: 4 <= noninit_count('num_deopt') < 50
# statcheck: 1 <= stats["num_osr_exits"] <= 2
try:
import __pyston__
__pyston__.setOption("OSR_THRESHOLD_BASELINE", 50)
__pyston__.setOption("REOPT_THRESHOLD_BASELINE", 50)
__pyston__.setOption("SPECULATION_THRESHOLD", 10)
except ImportError:
pass
def main():
class C(object):
def __repr__(self):
return "<C>"
def f_gen(o):
print "starting f_gen, yielding:"
yield 8
try:
print o.a
if o.b:
raise Exception('')
except Exception, e:
print o.c
print e
print o.d
print sorted(locals().items())
print "yielding again:"
yield 9
c = C()
c.a = 1
c.b = 0
c.c = 3
c.d = 4
for i in xrange(2000):
print i
if i == 500:
c.a = []
if i == 600:
c.b = 1
if i == 700:
c.c = []
if i == 800:
c.b = 0
c.d = 1.0
g = f_gen(c)
print 'yielded(1):', g.next()
print 'yielded(2):', g.next()
main()
# skip-if: '-O' in EXTRA_JIT_ARGS
# statcheck: 4 <= noninit_count('num_deopt') < 50
# statcheck: 1 <= stats["num_osr_exits"] <= 2
try:
import __pyston__
__pyston__.setOption("OSR_THRESHOLD_BASELINE", 50)
__pyston__.setOption("REOPT_THRESHOLD_BASELINE", 50)
__pyston__.setOption("SPECULATION_THRESHOLD", 10)
except ImportError:
pass
# This test makes sure that the boxedLocals survive a deopt.
# TODO Write a test case to make sure exc_info survives the deopt.
def f_with_name_scoping(o):
print "starting f"
exec "k = 5"
l = 6
try:
print o.a
if o.b:
raise Exception('')
except Exception, e:
print o.c
print e
print o.d
print sorted(locals().items())
print "k =", k
print l
print "Done"
def main():
class C(object):
def __repr__(self):
return "<C>"
c = C()
c.a = 1
c.b = 0
c.c = 3
c.d = 4
for i in xrange(300):
print i
if i == 60:
c.a = []
if i == 120:
c.b = 1
if i == 180:
c.c = []
if i == 240:
c.b = 0
c.d = 1.0
f_with_name_scoping(c)
main()
......@@ -11,8 +11,10 @@ except ImportError:
pass
def main():
var_in_closure1 = 0
def f(o):
print "starting"
print "starting f"
try:
print o.a
......@@ -24,6 +26,12 @@ def main():
print o.d
print sorted(locals().items())
print var_in_closure1
var_in_closure2 = 1
def g():
print var_in_closure2
g()
print "Done"
class C(object):
......@@ -54,8 +62,6 @@ def main():
f(c)
# Regression test reduced from subprocess.py:
import types
def f2(self, args):
......
......@@ -84,7 +84,14 @@ print eval("eval('3 + 2342')")
o = 300
print 'eval eval o', eval("eval('o')")
#print eval("[(lambda p : p + o)(5) for o in range(5)]")
# This works in the global scope but not in the local scope, because o1 is a global:
#print eval("[(lambda p1 : p1 + o1)(5) for o1 in range(5)]")
def lambda_func():
try:
pass #print eval("[(lambda p2 : p2 + o2)(5) for o2 in range(5)]")
except NameError as e:
print e.message
lambda_func()
shadow1 = 1000
shadow2 = 1000
......
exec """print 'hi'
a = 5
print a"""
exec ""
# We need to be able to parse files with exec statements in them,
# even if we don't support the actual exec statement yet.
def dont_run_me():
exec "1/0"
def f():
exec "a = 5"
print a
f()
# expected: fail
def f():
print eval("[a for a in xrange(2)]")
print eval("a")
f()
def f():
a = 0
b = 0
e = 0
r = 0
def g():
def h():
print b
print e
print a
c = 0
print c
eval("[a for a in xrange(2)]")
eval("[c for c in xrange(2)]")
eval("[d for d in xrange(2)]")
eval("[e for e in xrange(2)]")
print a
print c
# d not found, because it's read as a stack variable
try:
print d
except NameError:
print 'd not found'
# but d it *is* in the locals()
# On the other hand, a, c, and e don't get over-written
# and b appears even though it only gets passed-through.
# So it should look like:
# a:0, b:0, c:0, d:2, e:0
print locals()
def unused():
print r
g()
f()
def meh(l):
l['a'] = 5
return 3
def f():
print eval("meh(locals()) + a")
f()
def f():
print eval("meh(locals()) + a", globals(), {})
f()
def f():
d = locals()
a = 2
d['a'] = 3
print a
print d
f()
def f():
exec "print 'hi'"
d = locals()
a = 2
d['a'] = 3
print a
print d
f()
def f():
d = locals()
a = 2
d['a'] = 3
print a
print d
exec "print 'hi'"
f()
def f(arg):
a = 2
d = locals()
print d
a = 3
print d
locals()
print d
del a
print d
locals()
print d
f(12)
def f(arg):
exec "r = 12"
a = 2
d = locals()
print d
a = 3
print d
locals()
print d
del a
print d
locals()
print d
f(12)
def f():
a = 5
def g():
print a
print locals()
f()
def f():
def g():
a = 0
def h():
print a
print locals()
yield 12
print locals()
yield 13
yield h
a = 1
yield 2
gen = g()
h1 = gen.next()
hgen = h1()
hgen.next()
gen.next()
hgen.next()
f()
foo = 0
class C(object):
try:
del foo
except NameError:
print 'foo NameError'
foo = 0
class C(object):
foo = 1
print foo
del foo
print foo
class C(object):
a = 2
d = locals()
print d
a = 3
print d
locals()
print d
del a
print d
def f(moo):
class C(object):
a = 2
d = locals()
print d
a = 3
print d
locals()
print d
del a
print d
print moo
f(2134)
some_glob = 2
def f():
global some_glob
def g():
exec "some_glob = 5"
print some_glob
g()
f()
some_glob = 2
def f():
def g():
global some_glob
exec "some_glob = 5"
print some_glob
g()
f()
some_glob = 2
def f():
global some_glob
exec "some_glob = 5"
def g():
print some_glob
g()
f()
# We could just have a file for each, but if we Do these in execs,
# we don't need separate files for each, and that makes it easier
# to just spam all the permutations.
# The logic beyond this error message is oddly complicated.
cases = [
# protip: delete this first """ to get your editor to syntax-highlight the code
"""
# This should compile fine
def f():
a = 0
exec "b = 0"
""", """
# This should compile fine
def addpackage(sitedir, name, known_paths):
print a
exec "b = 0"
""", """
def f():
exec "a = 5"
def g():
print a
""", """
def f():
exec "a = 5"
def g():
def h():
print a
""", """
def f():
exec "a = 5"
class C(object):
def h():
print a
""", """
def f():
exec "a = 5"
def g():
b = 2
def h():
print b
""", """
def f():
def g():
print a
exec "a = 5"
""", """
def f():
from string import *
def g():
print a
""", """
def f():
from string import *
def g():
def h():
print a
""", """
def f():
from string import *
class C(object):
def h():
print a
""", """
def f():
from string import *
def g():
b = 2
def h():
print b
""", """
def f():
def g():
print a
from string import *
""", """
def f():
exec "a = 5"
from string import *
def g():
print a
""", """
def f():
from string import *
exec "a = 5"
def g():
print a
""", """
def f():
def g():
print a
from string import *
exec "a = 5"
""", """
def f():
def g():
print a
exec "a = 5"
from string import *
""", """
def f():
def g():
exec "a = 5"
""", """
class C(object):
def g():
a = 5
exec "a = 5"
""", """
class C(object):
def g():
exec "a = 5"
""", """
def f():
exec "a = 5"
return {b for b in xrange(3)}
""", """
def f():
exec "a = 5"
return [b for b in xrange(3)]
""", """
def f():
exec "a = 5"
return {b:b for b in xrange(3)}
""", """
def f():
exec "a = 5"
return {c for b in xrange(3)}
""", """
def f():
exec "a = 5"
return [c for b in xrange(3)]
""", """
def f():
exec "a = 5"
return {c:b for b in xrange(3)}
""", """
def f():
global a
def g():
print a
exec ""
""", """
def f():
global a
exec ""
def g():
print a
""", """
def f():
global a
def g():
a = 0
def h():
exec ""
print a
""", """
def f():
a = 0
def g():
global a
def h():
exec ""
print a
""", """
def f():
a = 0
def g():
exec ""
def h():
print a
"""
]
#import traceback
for case in cases:
print case
try:
exec case
except SyntaxError as se:
print se.message
# TODO uncomment this
# traceback.print_exc()
......@@ -77,6 +77,25 @@ class MyClass(object):
import sys as __bar
print __bar
class MyClass(object):
try:
from __foo import __bar
except ImportError, e:
print e
#TODO enable this once we support `import *` in functions
#class MyClass(object):
# try:
# from __foo import *
# except ImportError, e:
# print e
class MyClass(object):
try:
from sys import __bar
except ImportError, e:
print e
class MyClass(object):
try:
# Except if it's a dotted name:
......
try:
import __pyston__
__pyston__.setOption("OSR_THRESHOLD_BASELINE", 50)
except ImportError:
pass
def f1(x):
exec """
for i in xrange(x):
pass
print x
"""
f1(200)
def f3():
exec """
def f2(x):
def inner():
return x
return inner
"""
g = f2(200)
for i in xrange(200):
g()
print g()
f3()
......@@ -26,14 +26,14 @@ TEST_F(AnalysisTest, augassign) {
AST_Module* module = caching_parse_file(fn.c_str());
assert(module);
ScopingAnalysis *scoping = runScopingAnalysis(module);
ScopingAnalysis *scoping = new ScopingAnalysis(module);
assert(module->body[0]->type == AST_TYPE::FunctionDef);
AST_FunctionDef* func = static_cast<AST_FunctionDef*>(module->body[0]);
ScopeInfo* scope_info = scoping->getScopeInfoForNode(func);
ASSERT_FALSE(scope_info->refersToGlobal(module->interned_strings->get("a")));
ASSERT_FALSE(scope_info->refersToGlobal(module->interned_strings->get("b")));
ASSERT_FALSE(scope_info->getScopeTypeOfName(module->interned_strings->get("a")) == ScopeInfo::VarScopeType::GLOBAL);
ASSERT_FALSE(scope_info->getScopeTypeOfName(module->interned_strings->get("b")) == ScopeInfo::VarScopeType::GLOBAL);
SourceInfo* si = new SourceInfo(createModule("__main__", fn), scoping, func, func->body);
......
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