Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle scope correctly for @isdefined #22281

Merged
merged 1 commit into from
Jun 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Julia v0.7.0 Release Notes
New language features
---------------------

* Local variables can be tested for being defined
using the new `@isdefined variable` macro ([#TBD]).

Language changes
----------------

Expand Down
38 changes: 34 additions & 4 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ add_tfunc(===, 2, 2,
end
return Bool
end)
add_tfunc(isdefined, 1, IInf, (args...)->Bool)
add_tfunc(isdefined, 1, 2, (args...)->Bool)
add_tfunc(Core.sizeof, 1, 1, x->Int)
add_tfunc(nfields, 1, 1,
function (x::ANY)
Expand Down Expand Up @@ -2064,6 +2064,25 @@ function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState)
return abstract_eval_constant(e.args[1])
elseif e.head === :invoke
error("type inference data-flow error: tried to double infer a function")
elseif e.head === :isdefined
sym = e.args[1]
t = Bool
if isa(sym, Slot)
vtyp = vtypes[slot_id(sym)]
if vtyp.typ === Bottom
t = Const(false) # never assigned previously
elseif !vtyp.undef
t = Const(true) # definitely assigned previously
end
elseif isa(sym, Symbol)
if isdefined(sv.mod, sym.name)
t = Const(true)
end
elseif isa(sym, GlobalRef)
if isdefined(sym.mod, sym.name)
t = Const(true)
end
end
else
t = Any
end
Expand Down Expand Up @@ -4342,6 +4361,10 @@ const corenumtype = Union{Int32, Int64, Float32, Float64}
# return inlined replacement for `e`, inserting new needed statements
# at index `ins` in `stmts`.
function inlining_pass(e::Expr, sv::InferenceState, stmts, ins)
if e.head === :isdefined
isa(e.typ, Const) && return e.typ.val
return e
end
if e.head === :method
# avoid running the inlining pass on function definitions
return e
Expand Down Expand Up @@ -4654,15 +4677,22 @@ function replace_vars!(src::CodeInfo, r::ObjectIdDict)
end

function _replace_vars!(e::ANY, r::ObjectIdDict)
if isa(e,SSAValue) || isa(e,Slot)
if isa(e, SSAValue) || isa(e, Slot)
v = normvar(e)
if haskey(r, v)
return r[v]
end
end
if isa(e,Expr)
if isa(e, Expr)
for i = 1:length(e.args)
e.args[i] = _replace_vars!(e.args[i], r)
a = e.args[i]
if e.head === :isdefined
if e.args[i] !== _replace_vars!(a, r)
return true
end
else
e.args[i] = _replace_vars!(a, r)
end
end
end
return e
Expand Down
4 changes: 2 additions & 2 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,10 @@ isconst(m::Module, s::Symbol) =
"""
@isdefined s -> Bool

Tests whether symbol `s` is defined in the current scope.
Tests whether variable `s` is defined in the current scope.
"""
macro isdefined(s::Symbol)
return :(isdefined($__module__, $(QuoteNode(s))))
return Expr(:isdefined, esc(s))
end

# return an integer such that object_id(x)==object_id(y) if x===y
Expand Down
2 changes: 2 additions & 0 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jl_sym_t *inert_sym; jl_sym_t *vararg_sym;
jl_sym_t *unused_sym; jl_sym_t *static_parameter_sym;
jl_sym_t *polly_sym; jl_sym_t *inline_sym;
jl_sym_t *propagate_inbounds_sym;
jl_sym_t *isdefined_sym;

static uint8_t flisp_system_image[] = {
#include <julia_flisp.boot.inc>
Expand Down Expand Up @@ -431,6 +432,7 @@ void jl_init_frontend(void)
polly_sym = jl_symbol("polly");
inline_sym = jl_symbol("inline");
propagate_inbounds_sym = jl_symbol("propagate_inbounds");
isdefined_sym = jl_symbol("isdefined");
}

JL_DLLEXPORT void jl_lisp_prompt(void)
Expand Down
2 changes: 1 addition & 1 deletion src/ccall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ static bool runtime_sym_gvs(const char *f_lib, const char *f_name, MT &&M,
}
}
if (libsym == NULL) {
libsym = *(void**)jl_get_global(libptrgv);
libsym = *(void**)jl_get_globalvar(libptrgv);
}
assert(libsym != NULL);

Expand Down
88 changes: 80 additions & 8 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ static Function *jlboundserrorv_func;
static Function *jlcheckassign_func;
static Function *jldeclareconst_func;
static Function *jlgetbindingorerror_func;
static Function *jlboundp_func;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is "boundp" supposed to mean?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't name the functions, I just use them 😆

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow that's old 691b8be and used in a grand total of 3 places before this PR - maybe it should get a new name that says what it does?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

boundp is a common lisp function for testing whether a variable is bound: http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/fun_boundp.html

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that explains where the name came from, but doesn't help anyone reading the code who doesn't already know that figure out at a glance what it does

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent of my comment was to explain where the name came from.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the conclusion, do I need to add renaming of this function to the PR, or is this gtg?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not urgent, we should do a naming-cleanup-pass of the exported API before 1.0 and the unexported APIs after

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the renaming can be done separately.

static Function *jltopeval_func;
static Function *jlcopyast_func;
static Function *jltuple_func;
Expand Down Expand Up @@ -2325,7 +2326,6 @@ static void simple_escape_analysis(jl_value_t *expr, bool esc, jl_codectx_t *ctx
}
}
else if (e->head == foreigncall_sym) {
esc = true;
simple_escape_analysis(jl_exprarg(e, 0), esc, ctx);
// 2nd and 3d arguments are static
size_t alen = jl_array_dim0(e->args);
Expand All @@ -2334,20 +2334,22 @@ static void simple_escape_analysis(jl_value_t *expr, bool esc, jl_codectx_t *ctx
}
}
else if (e->head == method_sym) {
simple_escape_analysis(jl_exprarg(e,0), esc, ctx);
simple_escape_analysis(jl_exprarg(e, 0), esc, ctx);
if (jl_expr_nargs(e) > 1) {
simple_escape_analysis(jl_exprarg(e,1), esc, ctx);
simple_escape_analysis(jl_exprarg(e,2), esc, ctx);
simple_escape_analysis(jl_exprarg(e, 1), esc, ctx);
simple_escape_analysis(jl_exprarg(e, 2), esc, ctx);
}
}
else if (e->head == assign_sym) {
// don't consider assignment LHS as a variable "use"
simple_escape_analysis(jl_exprarg(e,1), esc, ctx);
simple_escape_analysis(jl_exprarg(e, 1), esc, ctx);
}
else if (e->head != line_sym) {
if (e->head == isdefined_sym)
esc = false;
size_t elen = jl_array_dim0(e->args);
for(i=0; i < elen; i++) {
simple_escape_analysis(jl_exprarg(e,i), esc, ctx);
for (i = 0; i < elen; i++) {
simple_escape_analysis(jl_exprarg(e, i), esc, ctx);
}
}
return;
Expand Down Expand Up @@ -3603,6 +3605,67 @@ static jl_cgval_t emit_global(jl_sym_t *sym, jl_codectx_t *ctx)
return emit_checked_var(bp, sym, ctx, false, tbaa_binding);
}

static jl_cgval_t emit_isdefined(jl_value_t *sym, jl_codectx_t *ctx)
{
Value *isnull;
if (jl_is_slot(sym)) {
size_t sl = jl_slot_number(sym) - 1;
jl_varinfo_t &vi = ctx->slots[sl];
if (!vi.usedUndef)
return mark_julia_const(jl_true);
if (vi.boxroot == NULL || vi.pTIndex != NULL) {
assert(vi.defFlag);
isnull = builder.CreateLoad(vi.defFlag, vi.isVolatile);
}
if (vi.boxroot != NULL) {
Value *boxed = builder.CreateLoad(vi.boxroot, vi.isVolatile);
Value *box_isnull = builder.CreateICmpNE(boxed, V_null);
if (vi.pTIndex) {
// value is either boxed in the stack slot, or unboxed in value
// as indicated by testing (pTIndex & 0x80)
Value *tindex = builder.CreateLoad(vi.pTIndex, vi.isVolatile);
Value *load_unbox = builder.CreateICmpEQ(
builder.CreateAnd(tindex, ConstantInt::get(T_int8, 0x80)),
ConstantInt::get(T_int8, 0));
isnull = builder.CreateSelect(load_unbox, isnull, box_isnull);
}
else {
isnull = box_isnull;
}
}
}
else {
jl_module_t *modu;
jl_sym_t *name;
if (jl_is_globalref(sym)) {
modu = jl_globalref_mod(sym);
name = jl_globalref_name(sym);
}
else {
assert(jl_is_symbol(sym) && "malformed isdefined expression");
modu = ctx->module;
name = (jl_sym_t*)sym;
}
jl_binding_t *bnd = jl_get_binding(modu, name);
if (bnd) {
if (bnd->value != NULL)
return mark_julia_const(jl_true);
Value *bp = julia_binding_gv(bnd);
Instruction *v = builder.CreateLoad(bp);
tbaa_decorate(tbaa_binding, v);
isnull = builder.CreateICmpNE(v, V_null);
}
else {
Value *v = builder.CreateCall(prepare_call(jlboundp_func), {
literal_pointer_val((jl_value_t*)modu),
literal_pointer_val((jl_value_t*)name)
});
isnull = builder.CreateICmpNE(v, V_null);
}
}
return mark_julia_type(isnull, false, jl_bool_type, ctx);
}

static jl_cgval_t emit_local(jl_value_t *slotload, jl_codectx_t *ctx)
{
size_t sl = jl_slot_number(slotload) - 1;
Expand Down Expand Up @@ -4131,7 +4194,10 @@ static jl_cgval_t emit_expr(jl_value_t *expr, jl_codectx_t *ctx)
// this is object-disoriented.
// however, this is a good way to do it because it should *not* be easy
// to add new node types.
if (head == invoke_sym) {
if (head == isdefined_sym) {
return emit_isdefined(args[0], ctx);
}
else if (head == invoke_sym) {
return emit_invoke(ex, ctx);
}
else if (head == call_sym) {
Expand Down Expand Up @@ -6774,6 +6840,12 @@ static void init_julia_llvm_env(Module *m)
"jl_get_binding_or_error", m);
add_named_global(jlgetbindingorerror_func, &jl_get_binding_or_error);

jlboundp_func =
Function::Create(FunctionType::get(T_pjlvalue, args_2ptrs, false),
Function::ExternalLinkage,
"jl_boundp", m);
add_named_global(jlboundp_func, &jl_boundp);

builtin_func_map[jl_f_is] = jlcall_func_to_llvm("jl_f_is", &jl_f_is, m);
builtin_func_map[jl_f_typeof] = jlcall_func_to_llvm("jl_f_typeof", &jl_f_typeof, m);
builtin_func_map[jl_f_sizeof] = jlcall_func_to_llvm("jl_f_sizeof", &jl_f_sizeof, m);
Expand Down
2 changes: 1 addition & 1 deletion src/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -3347,7 +3347,7 @@ void jl_init_serializer(void)

jl_emptysvec, jl_emptytuple, jl_false, jl_true, jl_nothing, jl_any_type,
call_sym, invoke_sym, goto_ifnot_sym, return_sym, body_sym, line_sym,
lambda_sym, jl_symbol("tuple"), assign_sym,
lambda_sym, jl_symbol("tuple"), assign_sym, isdefined_sym,

// empirical list of very common symbols
#include "common_symbols1.inc"
Expand Down
35 changes: 24 additions & 11 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,33 +203,46 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s)
ssize_t n = jl_slot_number(e);
if (src == NULL || n > jl_source_nslots(src) || n < 1 || s->locals == NULL)
jl_error("access to invalid slot number");
jl_value_t *v = s->locals[n-1];
jl_value_t *v = s->locals[n - 1];
if (v == NULL)
jl_undefined_var_error((jl_sym_t*)jl_array_ptr_ref(src->slotnames, n - 1));
return v;
}
if (jl_is_globalref(e)) {
jl_sym_t *s = jl_globalref_name(e);
jl_value_t *v = jl_get_global(jl_globalref_mod(e), s);
if (v == NULL)
jl_undefined_var_error(s);
return v;
return jl_eval_global_var(jl_globalref_mod(e), jl_globalref_name(e));
}
if (jl_is_quotenode(e))
return jl_fieldref(e,0);
jl_module_t *modu = s->module;
if (jl_is_symbol(e)) { // bare symbols appear in toplevel exprs not wrapped in `thunk`
jl_value_t *v = jl_get_global(modu, (jl_sym_t*)e);
if (v == NULL)
jl_undefined_var_error((jl_sym_t*)e);
return v;
return jl_eval_global_var(modu, (jl_sym_t*)e);
}
if (!jl_is_expr(e))
return e;
jl_expr_t *ex = (jl_expr_t*)e;
jl_value_t **args = (jl_value_t**)jl_array_data(ex->args);
size_t nargs = jl_array_len(ex->args);
if (ex->head == call_sym) {
if (ex->head == isdefined_sym) {
jl_value_t *sym = args[0];
int defined = 0;
if (jl_is_slot(sym)) {
ssize_t n = jl_slot_number(sym);
if (src == NULL || n > jl_source_nslots(src) || n < 1 || s->locals == NULL)
jl_error("access to invalid slot number");
defined = s->locals[n - 1] != NULL;
}
else if (jl_is_globalref(sym)) {
defined = jl_boundp(jl_globalref_mod(sym), jl_globalref_name(sym));
}
else if (jl_is_symbol(sym)) {
defined = jl_boundp(modu, (jl_sym_t*)sym);
}
else {
assert(0 && "malformed isdefined expression");
}
return defined ? jl_true : jl_false;
}
else if (ex->head == call_sym) {
return do_call(args, nargs, s);
}
else if (ex->head == invoke_sym) {
Expand Down
2 changes: 1 addition & 1 deletion src/jitlayers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,7 @@ GlobalVariable *jl_emit_sysimg_slot(Module *m, Type *typ, const char *name,
return gv;
}

void* jl_get_global(GlobalVariable *gv)
void* jl_get_globalvar(GlobalVariable *gv)
{
#if defined(USE_MCJIT) || defined(USE_ORCJIT)
void *p = (void*)(intptr_t)jl_ExecutionEngine->getPointerToGlobalIfAvailable(gv);
Expand Down
2 changes: 1 addition & 1 deletion src/jitlayers.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ void addOptimizationPasses(PassManager *PM);
void* jl_emit_and_add_to_shadow(GlobalVariable *gv, void *gvarinit = NULL);
GlobalVariable *jl_emit_sysimg_slot(Module *m, Type *typ, const char *name,
uintptr_t init, size_t &idx);
void* jl_get_global(GlobalVariable *gv);
void* jl_get_globalvar(GlobalVariable *gv);
GlobalVariable *jl_get_global_for(const char *cname, void *addr, Module *M);
void jl_add_to_shadow(Module *m);
void jl_init_function(Function *f);
Expand Down
19 changes: 19 additions & 0 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -3073,6 +3073,24 @@ f(x) = yt(x)
(assq (cadr e) (cadr (lam:vinfo lam))))
'(null)
e))
((isdefined) ;; convert isdefined expr to function for closure converted variables
(let* ((sym (cadr e))
(vi (and (symbol? sym) (assq sym (car (lam:vinfo lam)))))
(cv (and (symbol? sym) (assq sym (cadr (lam:vinfo lam))))))
(cond ((eq? sym fname) e)
((memq sym (lam:sp lam)) e)
(cv
(if (and (vinfo:asgn cv) (vinfo:capt cv))
(let ((access (if interp
`($ (call (core QuoteNode) ,sym))
`(call (core getfield) ,fname (inert ,sym)))))
`(call (core isdefined) ,access (inert contents)))
'true))
(vi
(if (and (vinfo:asgn vi) (vinfo:capt vi))
`(call (core isdefined) ,sym (inert contents))
e))
(else e))))
((method)
(let* ((name (method-expr-name e))
(short (length= e 2)) ;; function f end
Expand Down Expand Up @@ -3597,6 +3615,7 @@ f(x) = yt(x)
((local) #f)
((implicit-global) #f)
((const) (emit e))
((isdefined) (if tail (emit-return e) e))

;; top level expressions returning values
((abstract_type bits_type composite_type thunk toplevel module)
Expand Down
1 change: 1 addition & 0 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,7 @@ extern jl_sym_t *meta_sym; extern jl_sym_t *list_sym;
extern jl_sym_t *inert_sym; extern jl_sym_t *static_parameter_sym;
extern jl_sym_t *polly_sym; extern jl_sym_t *inline_sym;
extern jl_sym_t *propagate_inbounds_sym;
extern jl_sym_t *isdefined_sym;

#ifdef __cplusplus
}
Expand Down
Loading