diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 49cf0a37..7ed1133e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -19,7 +19,6 @@ jobs: fail-fast: false matrix: version: - - '~1.12.0-rc1' - 'nightly' os: - ubuntu-latest diff --git a/README.md b/README.md index 0918fb0f..a4207027 100644 --- a/README.md +++ b/README.md @@ -898,8 +898,6 @@ In the current Julia runtime, * `:import` * `:public` * `:export` - * `:global` - * `:const` * `:toplevel` * `:error` * `:incomplete` diff --git a/src/closure_conversion.jl b/src/closure_conversion.jl index d2820c08..99aecd3f 100644 --- a/src/closure_conversion.jl +++ b/src/closure_conversion.jl @@ -18,6 +18,11 @@ struct ClosureConversionCtx{GraphType} <: AbstractLoweringContext # True if we're in a section of code which preserves top-level sequencing # such that closure types can be emitted inline with other code. is_toplevel_seq_point::Bool + # True if this expression should not have toplevel effects, namely, it + # should not declare the globals it references. This allows generated + # functions to refer to globals that have already been declared, without + # triggering the "function body AST not pure" error. + toplevel_pure::Bool toplevel_stmts::SyntaxList{GraphType} closure_infos::Dict{IdTag,ClosureInfo{GraphType}} end @@ -27,7 +32,8 @@ function ClosureConversionCtx(graph::GraphType, bindings::Bindings, lambda_bindings::LambdaBindings) where {GraphType} ClosureConversionCtx{GraphType}( graph, bindings, mod, closure_bindings, nothing, - lambda_bindings, false, SyntaxList(graph), Dict{IdTag,ClosureInfo{GraphType}}()) + lambda_bindings, false, true, SyntaxList(graph), + Dict{IdTag,ClosureInfo{GraphType}}()) end function current_lambda_bindings(ctx::ClosureConversionCtx) @@ -117,10 +123,39 @@ function convert_for_type_decl(ctx, srcref, ex, type, do_typeassert) ] end +# TODO: Avoid producing redundant calls to declare_global +function make_globaldecl(ctx, src_ex, mod, name, strong=false, type=nothing; ret_nothing=false) + if !ctx.toplevel_pure + decl = @ast ctx src_ex [K"block" + [K"call" + "declare_global"::K"core" + mod::K"Value" name::K"Symbol" strong::K"Bool" + if type !== nothing + type + end + ] + [K"latestworld"] + @ast ctx src_ex [K"removable" "nothing"::K"core"] + ] + if ctx.is_toplevel_seq_point + return decl + else + push!(ctx.toplevel_stmts, decl) + end + end + if ret_nothing + nothing + else + @ast ctx src_ex [K"removable" "nothing"::K"core"] + end +end + function convert_global_assignment(ctx, ex, var, rhs0) binfo = lookup_binding(ctx, var) @assert binfo.kind == :global stmts = SyntaxList(ctx) + decl = make_globaldecl(ctx, ex, binfo.mod, binfo.name, true; ret_nothing=true) + decl !== nothing && push!(stmts, decl) rhs1 = if is_simple_atom(ctx, rhs0) rhs0 else @@ -147,7 +182,6 @@ function convert_global_assignment(ctx, ex, var, rhs0) end push!(stmts, @ast ctx ex [K"=" var rhs]) @ast ctx ex [K"block" - [K"globaldecl" var] stmts... rhs1 ] @@ -296,7 +330,7 @@ function map_cl_convert(ctx::ClosureConversionCtx, ex, toplevel_preserving) toplevel_stmts = SyntaxList(ctx) ctx2 = ClosureConversionCtx(ctx.graph, ctx.bindings, ctx.mod, ctx.closure_bindings, ctx.capture_rewriting, ctx.lambda_bindings, - false, toplevel_stmts, ctx.closure_infos) + false, ctx.toplevel_pure, toplevel_stmts, ctx.closure_infos) res = mapchildren(e->_convert_closures(ctx2, e), ctx2, ex) if isempty(toplevel_stmts) res @@ -352,16 +386,24 @@ function _convert_closures(ctx::ClosureConversionCtx, ex) @assert kind(ex[1]) == K"BindingId" binfo = lookup_binding(ctx, ex[1]) if binfo.kind == :global - @ast ctx ex [K"block" - # flisp has this, but our K"assert" handling is in a previous pass - # [K"assert" "toplevel_only"::K"Symbol" [K"inert" ex]] - [K"globaldecl" - ex[1] - _convert_closures(ctx, ex[2])] - "nothing"::K"core"] + # flisp has this, but our K"assert" handling is in a previous pass + # [K"assert" "toplevel_only"::K"Symbol" [K"inert" ex]] + make_globaldecl(ctx, ex, binfo.mod, binfo.name, true, _convert_closures(ctx, ex[2])) else makeleaf(ctx, ex, K"TOMBSTONE") end + elseif k == K"global" + # Leftover `global` forms become weak globals. + mod, name = if kind(ex[1]) == K"BindingId" + binfo = lookup_binding(ctx, ex[1]) + @assert binfo.kind == :global + binfo.mod, binfo.name + else + # See note about using eval on Expr(:global/:const, GlobalRef(...)) + @assert ex[1].value isa GlobalRef + ex[1].value.mod, String(ex[1].value.name) + end + @ast ctx ex [K"unused_only" make_globaldecl(ctx, ex, mod, name, false)] elseif k == K"local" var = ex[1] binfo = lookup_binding(ctx, var) @@ -453,7 +495,8 @@ function _convert_closures(ctx::ClosureConversionCtx, ex) cap_rewrite = is_closure ? ctx.closure_infos[name.var_id] : nothing ctx2 = ClosureConversionCtx(ctx.graph, ctx.bindings, ctx.mod, ctx.closure_bindings, cap_rewrite, ctx.lambda_bindings, - ctx.is_toplevel_seq_point, ctx.toplevel_stmts, ctx.closure_infos) + ctx.is_toplevel_seq_point, ctx.toplevel_pure, ctx.toplevel_stmts, + ctx.closure_infos) body = map_cl_convert(ctx2, ex[2], false) if is_closure if ctx.is_toplevel_seq_point @@ -478,7 +521,7 @@ function _convert_closures(ctx::ClosureConversionCtx, ex) ctx2 = ClosureConversionCtx(ctx.graph, ctx.bindings, ctx.mod, ctx.closure_bindings, capture_rewrites, ctx.lambda_bindings, - false, ctx.toplevel_stmts, ctx.closure_infos) + false, ctx.toplevel_pure, ctx.toplevel_stmts, ctx.closure_infos) init_closure_args = SyntaxList(ctx) for id in field_orig_bindings @@ -521,7 +564,8 @@ function closure_convert_lambda(ctx, ex) end ctx2 = ClosureConversionCtx(ctx.graph, ctx.bindings, ctx.mod, ctx.closure_bindings, cap_rewrite, lambda_bindings, - ex.is_toplevel_thunk, ctx.toplevel_stmts, ctx.closure_infos) + ex.is_toplevel_thunk, ctx.toplevel_pure && ex.toplevel_pure, + ctx.toplevel_stmts, ctx.closure_infos) lambda_children = SyntaxList(ctx) args = ex[1] push!(lambda_children, args) diff --git a/src/compat.jl b/src/compat.jl index bb2edc3e..97c69f35 100644 --- a/src/compat.jl +++ b/src/compat.jl @@ -25,7 +25,8 @@ function expr_to_syntaxtree(@nospecialize(e), lnn::Union{LineNumberNode, Nothing kind=Kind, syntax_flags=UInt16, source=SourceAttrType, var_id=Int, value=Any, name_val=String, is_toplevel_thunk=Bool, - scope_layer=LayerId, meta=CompileHints) + scope_layer=LayerId, meta=CompileHints, + toplevel_pure=Bool) expr_to_syntaxtree(graph, e, lnn) end diff --git a/src/desugaring.jl b/src/desugaring.jl index 84ccbac9..716a8a60 100644 --- a/src/desugaring.jl +++ b/src/desugaring.jl @@ -15,7 +15,8 @@ function DesugaringContext(ctx, expr_compat_mode::Bool) value=Any, name_val=String, scope_type=Symbol, # :hard or :soft var_id=IdTag, - is_toplevel_thunk=Bool) + is_toplevel_thunk=Bool, + toplevel_pure=Bool) DesugaringContext(graph, ctx.bindings, ctx.scope_layers, @@ -108,9 +109,10 @@ end # constructed followed by destructuring. In particular, any side effects due to # evaluating the individual terms in the right hand side tuple must happen in # order. -function tuple_to_assignments(ctx, ex) +function tuple_to_assignments(ctx, ex, is_const) lhs = ex[1] rhs = ex[2] + wrap(asgn) = is_const ? (@ast ctx ex [K"const" asgn]) : asgn # Tuple elimination aims to turn assignments between tuples into lists of assignments. # @@ -187,12 +189,12 @@ function tuple_to_assignments(ctx, ex) # (x, ys...) = (a,b,c) # (x, ys...) = (a,bs...) # (ys...) = () - push!(stmts, @ast ctx ex [K"=" lh[1] middle]) + push!(stmts, wrap(@ast ctx ex [K"=" lh[1] middle])) else # (x, ys..., z) = (a, b, c, d) # (x, ys..., z) = (a, bs...) # (xs..., y) = (a, bs...) - push!(stmts, @ast ctx ex [K"=" [K"tuple" lhs[il:jl]...] middle]) + push!(stmts, wrap(@ast ctx ex [K"=" [K"tuple" lhs[il:jl]...] middle])) end # Continue with the remainder of the list of non-splat terms il = jl @@ -200,10 +202,10 @@ function tuple_to_assignments(ctx, ex) else rh = rhs_tmps[ir] if kind(rh) == K"..." - push!(stmts, @ast ctx ex [K"=" [K"tuple" lhs[il:end]...] rh[1]]) + push!(stmts, wrap(@ast ctx ex [K"=" [K"tuple" lhs[il:end]...] rh[1]])) break else - push!(stmts, @ast ctx ex [K"=" lh rh]) + push!(stmts, wrap(@ast ctx ex [K"=" lh rh])) end end end @@ -275,23 +277,24 @@ end # Destructuring in this context is done via the iteration interface, though # calls `Base.indexed_iterate()` to allow for a fast path in cases where the # right hand side is directly indexable. -function _destructure(ctx, assignment_srcref, stmts, lhs, rhs) +function _destructure(ctx, assignment_srcref, stmts, lhs, rhs, is_const) n_lhs = numchildren(lhs) if n_lhs > 0 iterstate = new_local_binding(ctx, rhs, "iterstate") end end_stmts = SyntaxList(ctx) + wrap(asgn) = is_const ? (@ast ctx assignment_srcref [K"const" asgn]) : asgn i = 0 for lh in children(lhs) i += 1 if kind(lh) == K"..." - lh1 = if is_identifier_like(lh[1]) + lh1 = if is_identifier_like(lh[1]) && !is_const lh[1] else lhs_tmp = ssavar(ctx, lh[1], "lhs_tmp") - push!(end_stmts, expand_forms_2(ctx, @ast ctx lh[1] [K"=" lh[1] lhs_tmp])) + push!(end_stmts, expand_forms_2(ctx, wrap(@ast ctx lh[1] [K"=" lh[1] lhs_tmp]))) lhs_tmp end if i == n_lhs @@ -341,12 +344,12 @@ function _destructure(ctx, assignment_srcref, stmts, lhs, rhs) else # Normal case, eg, for `y` in # (x, y, z) = rhs - lh1 = if is_identifier_like(lh) + lh1 = if is_identifier_like(lh) && !is_const lh # elseif is_eventually_call(lh) (TODO??) else lhs_tmp = ssavar(ctx, lh, "lhs_tmp") - push!(end_stmts, expand_forms_2(ctx, @ast ctx lh [K"=" lh lhs_tmp])) + push!(end_stmts, expand_forms_2(ctx, wrap(@ast ctx lh [K"=" lh lhs_tmp]))) lhs_tmp end push!(stmts, @@ -374,7 +377,7 @@ function _destructure(ctx, assignment_srcref, stmts, lhs, rhs) end # Expands cases of property destructuring -function expand_property_destruct(ctx, ex) +function expand_property_destruct(ctx, ex, is_const) @assert numchildren(ex) == 2 lhs = ex[1] @assert kind(lhs) == K"tuple" @@ -405,7 +408,7 @@ end # Expands all cases of general tuple destructuring, eg # (x,y) = (a,b) -function expand_tuple_destruct(ctx, ex) +function expand_tuple_destruct(ctx, ex, is_const) lhs = ex[1] @assert kind(lhs) == K"tuple" rhs = ex[2] @@ -426,7 +429,7 @@ function expand_tuple_destruct(ctx, ex) if !any_assignment(children(rhs)) && !has_parameters(rhs) && _tuple_sides_match(children(lhs), children(rhs)) - return expand_forms_2(ctx, tuple_to_assignments(ctx, ex)) + return expand_forms_2(ctx, tuple_to_assignments(ctx, ex, is_const)) end end @@ -439,7 +442,7 @@ function expand_tuple_destruct(ctx, ex) else emit_assign_tmp(stmts, ctx, expand_forms_2(ctx, rhs)) end - _destructure(ctx, ex, stmts, lhs, rhs1) + _destructure(ctx, ex, stmts, lhs, rhs1, is_const) push!(stmts, @ast ctx rhs1 [K"removable" rhs1]) makenode(ctx, ex, K"block", stmts) end @@ -1218,7 +1221,11 @@ function expand_assignment(ctx, ex, is_const=false) lhs = ex[1] rhs = ex[2] kl = kind(lhs) - if kl == K"curly" + if kind(ex) == K"function" + # `const f() = ...` - The `const` here is inoperative, but the syntax + # happened to work in earlier versions, so simply strip `const`. + expand_forms_2(ctx, ex[1]) + elseif kl == K"curly" expand_unionall_def(ctx, ex, lhs, rhs, is_const) elseif kind(rhs) == K"=" # Expand chains of assignments @@ -1284,9 +1291,9 @@ function expand_assignment(ctx, ex, is_const=false) ] elseif kl == K"tuple" if has_parameters(lhs) - expand_property_destruct(ctx, ex) + expand_property_destruct(ctx, ex, is_const) else - expand_tuple_destruct(ctx, ex) + expand_tuple_destruct(ctx, ex, is_const) end elseif kl == K"ref" # a[i1, i2] = rhs @@ -2119,35 +2126,37 @@ end #------------------------------------------------------------------------------- # Expand local/global/const declarations -# Strip variable type declarations from within a `local` or `global`, returning -# the stripped expression. Works recursively with complex left hand side -# assignments containing tuple destructuring. Eg, given +# Create local/global declarations, and possibly type declarations for each name +# on an assignment LHS. Works recursively with complex left hand side +# assignments containing tuple destructuring. Eg, given # (x::T, (y::U, z)) # strip out stmts = (local x) (decl x T) (local x) (decl y U) (local z) # and return (x, (y, z)) -function strip_decls!(ctx, stmts, declkind, declmeta, ex) +function make_lhs_decls(ctx, stmts, declkind, declmeta, ex, type_decls=true) k = kind(ex) - if k == K"Identifier" + if k == K"Identifier" || k == K"Value" && ex.value isa GlobalRef + # TODO: consider removing support for Expr(:global, GlobalRef(...)) and + # other Exprs that cannot be produced by the parser (tested by + # test/precompile.jl #50538). if !isnothing(declmeta) push!(stmts, makenode(ctx, ex, declkind, ex; meta=declmeta)) else push!(stmts, makenode(ctx, ex, declkind, ex)) end - ex elseif k == K"Placeholder" - ex - elseif k == K"::" - @chk numchildren(ex) == 2 - name = ex[1] - @chk kind(name) == K"Identifier" - push!(stmts, makenode(ctx, ex, K"decl", name, ex[2])) - strip_decls!(ctx, stmts, declkind, declmeta, ex[1]) + nothing + elseif (k === K"::" && numchildren(ex) === 2) || k in KSet"call curly where" + if type_decls + @chk numchildren(ex) == 2 + name = ex[1] + @chk kind(name) == K"Identifier" + push!(stmts, makenode(ctx, ex, K"decl", name, ex[2])) + end + make_lhs_decls(ctx, stmts, declkind, declmeta, ex[1], type_decls) elseif k == K"tuple" || k == K"parameters" - cs = SyntaxList(ctx) for e in children(ex) - push!(cs, strip_decls!(ctx, stmts, declkind, declmeta, e)) + make_lhs_decls(ctx, stmts, declkind, declmeta, e, type_decls) end - makenode(ctx, ex, k, cs) else throw(LoweringError(ex, "invalid kind $k in $declkind declaration")) end @@ -2162,13 +2171,16 @@ function expand_decls(ctx, ex) bindings = children(ex) stmts = SyntaxList(ctx) for binding in bindings - kb = kind(binding) - if is_prec_assignment(kb) + if is_prec_assignment(kind(binding)) @chk numchildren(binding) == 2 - lhs = strip_decls!(ctx, stmts, declkind, declmeta, binding[1]) - push!(stmts, expand_assignment(ctx, @ast ctx binding [kb lhs binding[2]])) - elseif is_sym_decl(binding) - strip_decls!(ctx, stmts, declkind, declmeta, binding) + # expand_assignment will create the type decls + make_lhs_decls(ctx, stmts, declkind, declmeta, binding[1], false) + push!(stmts, expand_assignment(ctx, binding)) + elseif is_sym_decl(binding) || kind(binding) == K"Value" + make_lhs_decls(ctx, stmts, declkind, declmeta, binding, true) + elseif kind(binding) == K"function" + make_lhs_decls(ctx, stmts, declkind, declmeta, binding[1], false) + push!(stmts, expand_forms_2(ctx, binding)) else throw(LoweringError(ex, "invalid syntax in variable declaration")) end @@ -2198,7 +2210,7 @@ function expand_const_decl(ctx, ex) k = kind(ex[1]) if k == K"global" asgn = ex[1][1] - @chk (kind(asgn) == K"=") (ex, "expected assignment after `const`") + @chk (kind(asgn) == K"=" || kind(asgn) == K"function") (ex, "expected assignment after `const`") globals = SyntaxList(ctx) foreach_lhs_name(asgn[1]) do x push!(globals, @ast ctx ex [K"global" x]) @@ -2207,13 +2219,17 @@ function expand_const_decl(ctx, ex) globals... expand_assignment(ctx, asgn, true) ] - elseif k == K"=" - if numchildren(ex[1]) >= 1 && kind(ex[1][1]) == K"tuple" - TODO(ex[1][1], "`const` tuple assignment desugaring") - end + elseif k == K"=" || k == K"function" expand_assignment(ctx, ex[1], true) elseif k == K"local" throw(LoweringError(ex, "unsupported `const local` declaration")) + elseif k == K"Identifier" || k == K"Value" + # Expr(:const, v) where v is a Symbol or a GlobalRef is an unfortunate + # remnant from the days when const-ness was a flag that could be set on + # any global. It creates a binding with kind PARTITION_KIND_UNDEF_CONST. + # TODO: deprecate and delete this "feature" + @chk numchildren(ex) == 1 + @ast ctx ex [K"constdecl" ex[1]] else throw(LoweringError(ex, "expected assignment after `const`")) end @@ -2326,7 +2342,7 @@ function method_def_expr(ctx, srcref, callex_srcref, method_table, [K"method" isnothing(method_table) ? "nothing"::K"core" : method_table method_metadata - [K"lambda"(body, is_toplevel_thunk=false) + [K"lambda"(body, is_toplevel_thunk=false, toplevel_pure=false) [K"block" arg_names...] [K"block" typevar_names...] body @@ -3238,7 +3254,7 @@ function expand_opaque_closure(ctx, ex) nargs::K"Integer" is_va::K"Bool" ::K"SourceLocation"(func_expr) - [K"lambda"(func_expr, is_toplevel_thunk=false) + [K"lambda"(func_expr, is_toplevel_thunk=false, toplevel_pure=false) [K"block" arg_names...] [K"block"] [K"block" diff --git a/src/eval.jl b/src/eval.jl index fe859ae0..fcb914b2 100644 --- a/src/eval.jl +++ b/src/eval.jl @@ -425,12 +425,9 @@ function _to_lowered_expr(ex::SyntaxTree, stmt_offset::Int) k == K"new" ? :new : k == K"splatnew" ? :splatnew : k == K"=" ? :(=) : - k == K"global" ? :global : - k == K"constdecl" ? :const : k == K"leave" ? :leave : k == K"isdefined" ? :isdefined : k == K"latestworld" ? :latestworld : - k == K"globaldecl" ? :globaldecl : k == K"pop_exception" ? :pop_exception : k == K"captured_local" ? :captured_local : k == K"gc_preserve_begin" ? :gc_preserve_begin : diff --git a/src/kinds.jl b/src/kinds.jl index fc7afdd4..0f2dfbb4 100644 --- a/src/kinds.jl +++ b/src/kinds.jl @@ -126,6 +126,13 @@ function _register_kinds() "captured_local" # Causes the linearization pass to conditionally emit a world age increment "latestworld_if_toplevel" + # This has two forms: + # [K"constdecl" var val] => declare and assign constant + # [K"constdecl" var] => declare undefined constant + # var is GlobalRef Value or Identifier + "constdecl" + # Returned from statements that should error if the result is used. + "unused_only" "END_LOWERING_KINDS" # The following kinds are emitted by lowering and used in Julia's untyped IR @@ -138,10 +145,6 @@ function _register_kinds() "static_parameter" # References/declares a global variable within a module "globalref" - "globaldecl" - # Two-argument constant declaration and assignment. - # Translated to :const in the IR for now (we use K"const" already in parsing). - "constdecl" # Unconditional goto "goto" # Conditional goto diff --git a/src/linear_ir.jl b/src/linear_ir.jl index c1133145..01d11216 100644 --- a/src/linear_ir.jl +++ b/src/linear_ir.jl @@ -318,10 +318,10 @@ end # or K"constdecl". flisp: emit-assignment-or-setglobal function emit_simple_assignment(ctx, srcref, lhs, rhs, op=K"=") binfo = lookup_binding(ctx, lhs.var_id) - if binfo.kind == :global && op == K"=" + if binfo.kind == :global emit(ctx, @ast ctx srcref [ K"call" - "setglobal!"::K"core" + op == K"constdecl" ? "declare_const"::K"core" : "setglobal!"::K"core" binfo.mod::K"Value" binfo.name::K"Symbol" rhs @@ -614,6 +614,18 @@ function compile(ctx::LinearIRContext, ex, needs_value, in_tail_pos) lhs = ex[1] res = if kind(lhs) == K"Placeholder" compile(ctx, ex[2], needs_value, in_tail_pos) + elseif k == K"constdecl" && numchildren(ex) == 1 + # No RHS - make undefined constant + mod, name = if kind(ex[1]) == K"BindingId" + binfo = lookup_binding(ctx, ex[1]) + binfo.mod, binfo.name + else + @assert kind(ex[1]) == K"Value" && typeof(ex[1].value) === GlobalRef + gr = ex[1].value + gr.mod, String(gr.name) + end + emit(ctx, @ast ctx ex [K"call" "declare_const"::K"core" + mod::K"Value" name::K"Symbol"]) else rhs = compile(ctx, ex[2], true, false) # TODO look up arg-map for renaming if lhs was reassigned @@ -791,21 +803,6 @@ function compile(ctx::LinearIRContext, ex, needs_value, in_tail_pos) end emit(ctx, ex) nothing - elseif k == K"global" - emit(ctx, ex) - ctx.is_toplevel_thunk && emit_latestworld(ctx, ex) - if needs_value - if in_tail_pos && ctx.is_toplevel_thunk - # Permit "statement-like" globals at top level but potentially - # inside blocks. - compile(ctx, nothing_(ctx, ex), needs_value, in_tail_pos) - else - throw(LoweringError(ex, - "global declaration doesn't read the variable and can't return a value")) - end - else - nothing - end elseif k == K"meta" emit(ctx, ex) if needs_value @@ -851,17 +848,6 @@ function compile(ctx::LinearIRContext, ex, needs_value, in_tail_pos) # TODO: also exclude deleted vars emit(ctx, ex) end - elseif k == K"globaldecl" - if needs_value - throw(LoweringError(ex, "misplaced global declaration")) - end - if numchildren(ex) == 1 || is_identifier_like(ex[2]) - emit(ctx, ex) - else - rr = emit_assign_tmp(ctx, ex[2]) - emit(ctx, @ast ctx ex [K"globaldecl" ex[1] rr]) - end - ctx.is_toplevel_thunk && emit_latestworld(ctx, ex) elseif k == K"latestworld" if needs_value throw(LoweringError(ex, "misplaced latestsworld")) @@ -869,6 +855,12 @@ function compile(ctx::LinearIRContext, ex, needs_value, in_tail_pos) emit_latestworld(ctx, ex) elseif k == K"latestworld_if_toplevel" ctx.is_toplevel_thunk && emit_latestworld(ctx, ex) + elseif k == K"unused_only" + if needs_value && !(in_tail_pos && ctx.is_toplevel_thunk) + throw(LoweringError(ex, + "global declaration doesn't read the variable and can't return a value")) + end + compile(ctx, ex[1], needs_value, in_tail_pos) else throw(LoweringError(ex, "Invalid syntax; $(repr(k))")) end diff --git a/src/runtime.jl b/src/runtime.jl index ec175047..ff089c99 100644 --- a/src/runtime.jl +++ b/src/runtime.jl @@ -189,7 +189,7 @@ function eval_closure_type(mod::Module, closure_type_name::Symbol, field_names, false, length(field_names)) Core._setsuper!(type, Core.Function) - @ccall jl_set_const(mod::Module, closure_type_name::Symbol, type::Any)::Cvoid + Core.declare_const(mod, closure_type_name, type) Core._typebody!(false, type, Core.svec(field_types...)) type end @@ -331,7 +331,8 @@ function (g::GeneratedFunctionStub)(world::UInt, source::Method, @nospecialize a graph = ensure_attributes(ensure_macro_attributes(graph), # Additional attribute for resolve_scopes, for # adding our custom lambda below - is_toplevel_thunk=Bool + is_toplevel_thunk=Bool, + toplevel_pure=Bool, ) __module__ = source.module @@ -367,7 +368,7 @@ function (g::GeneratedFunctionStub)(world::UInt, source::Method, @nospecialize a ctx2, ex2 = expand_forms_2(ctx1, ex1) # Wrap expansion in a non-toplevel lambda and run scope resolution - ex2 = @ast ctx2 ex0 [K"lambda"(is_toplevel_thunk=false) + ex2 = @ast ctx2 ex0 [K"lambda"(is_toplevel_thunk=false, toplevel_pure=true) [K"block" (string(n)::K"Identifier" for n in g.argnames)... ] diff --git a/src/scope_analysis.jl b/src/scope_analysis.jl index 6f146b23..f73c7362 100644 --- a/src/scope_analysis.jl +++ b/src/scope_analysis.jl @@ -43,17 +43,21 @@ function _find_scope_vars!(ctx, assignments, locals, destructured_args, globals, _insert_if_not_present!(locals, NameKey(ex[1]), ex) end elseif k == K"global" - _insert_if_not_present!(globals, NameKey(ex[1]), ex) + if !(kind(ex[1]) == K"Value" && ex[1].value isa GlobalRef) + _insert_if_not_present!(globals, NameKey(ex[1]), ex) + end elseif k == K"assign_or_constdecl_if_global" # like v = val, except that if `v` turns out global(either implicitly or # by explicit `global`), it gains an implicit `const` _insert_if_not_present!(assignments, NameKey(ex[1]), ex) elseif k == K"=" || k == K"constdecl" v = decl_var(ex[1]) - if !(kind(v) in KSet"BindingId globalref Placeholder") + if !(kind(v) in KSet"BindingId globalref Value Placeholder") _insert_if_not_present!(assignments, NameKey(v), v) end - _find_scope_vars!(ctx, assignments, locals, destructured_args, globals, used_names, used_bindings, ex[2]) + if k != K"constdecl" || numchildren(ex) == 2 + _find_scope_vars!(ctx, assignments, locals, destructured_args, globals, used_names, used_bindings, ex[2]) + end elseif k == K"function_decl" v = ex[1] kv = kind(v) @@ -473,7 +477,8 @@ function _resolve_scopes(ctx, ex::SyntaxTree) pop!(ctx.scope_stack) @ast ctx ex [K"lambda"(lambda_bindings=scope.lambda_bindings, - is_toplevel_thunk=is_toplevel_thunk) + is_toplevel_thunk=is_toplevel_thunk, + toplevel_pure=false) arg_bindings sparm_bindings [K"block" @@ -720,10 +725,12 @@ function analyze_variables!(ctx, ex) end elseif k == K"constdecl" id = ex[1] - if lookup_binding(ctx, id).kind == :local - throw(LoweringError(ex, "unsupported `const` declaration on local variable")) + if kind(id) == K"BindingId" + if lookup_binding(ctx, id).kind == :local + throw(LoweringError(ex, "unsupported `const` declaration on local variable")) + end + update_binding!(ctx, id; is_const=true) end - update_binding!(ctx, id; is_const=true) elseif k == K"call" name = ex[1] if kind(name) == K"BindingId" @@ -786,7 +793,7 @@ function resolve_scopes(ctx::ScopeResolutionContext, ex) if kind(ex) != K"lambda" # Wrap in a top level thunk if we're not already expanding a lambda. # (Maybe this should be done elsewhere?) - ex = @ast ctx ex [K"lambda"(is_toplevel_thunk=true) + ex = @ast ctx ex [K"lambda"(is_toplevel_thunk=true, toplevel_pure=false) [K"block"] [K"block"] ex diff --git a/test/assignments_ir.jl b/test/assignments_ir.jl index 9789fc6e..4fcb0abb 100644 --- a/test/assignments_ir.jl +++ b/test/assignments_ir.jl @@ -142,7 +142,7 @@ X{T} = Y{T,T} 6 slot₁/T 7 (call core.apply_type %₄ %₅ %₆) 8 (call core.UnionAll %₃ %₇) -9 (constdecl TestMod.X %₈) +9 (call core.declare_const TestMod :X %₈) 10 latestworld 11 (return %₈) diff --git a/test/closures_ir.jl b/test/closures_ir.jl index 475eea8c..9f3d07e5 100644 --- a/test/closures_ir.jl +++ b/test/closures_ir.jl @@ -755,7 +755,7 @@ slots: [slot₁/#self#(!read) slot₂/T(!read) slot₃/tmp(!read)] # Error: Closure outside any top level context # (Should only happen in a user-visible way when lowering code emitted # from a `@generated` function code generator.) -@ast_ [K"lambda"(is_toplevel_thunk=false) +@ast_ [K"lambda"(is_toplevel_thunk=false, toplevel_pure=false) [K"block"] [K"block"] [K"->" [K"tuple"] [K"block"]] diff --git a/test/decls.jl b/test/decls.jl index 50aa98a5..ab25aaa0 100644 --- a/test/decls.jl +++ b/test/decls.jl @@ -93,8 +93,29 @@ end # Tuple/destructuring assignments @test JuliaLowering.include_string(test_mod, "(a0, a1, a2) = [1,2,3]") == [1,2,3] - -# Unsupported for now -@test_throws LoweringError JuliaLowering.include_string(test_mod, "const a,b,c = 1,2,3") +@test JuliaLowering.include_string(test_mod, "const a,b,c = 1,2,3") === (1, 2, 3) + +test_mod_2 = Module() +@testset "toplevel-preserving syntax" begin + JuliaLowering.include_string(test_mod_2, "if true; global v1::Bool; else const v1 = 1; end") + @test !isdefined(test_mod_2, :v1) + @test Base.binding_kind(test_mod_2, :v1) == Base.PARTITION_KIND_GLOBAL + @test Core.get_binding_type(test_mod_2, :v1) == Bool + + JuliaLowering.include_string(test_mod_2, "if false; global v2::Bool; else const v2 = 2; end") + @test test_mod_2.v2 === 2 + @test Base.binding_kind(test_mod_2, :v2) == Base.PARTITION_KIND_CONST + + JuliaLowering.include_string(test_mod_2, "v3 = if true; global v4::Bool; 4 else const v4 = 5; 6; end") + @test test_mod_2.v3 == 4 + @test !isdefined(test_mod_2, :v4) + @test Base.binding_kind(test_mod_2, :v4) == Base.PARTITION_KIND_GLOBAL + @test Core.get_binding_type(test_mod_2, :v4) == Bool + + JuliaLowering.include_string(test_mod_2, "v5 = if false; global v6::Bool; 4 else const v6 = 5; 6; end") + @test test_mod_2.v5 === 6 + @test test_mod_2.v6 === 5 + @test Base.binding_kind(test_mod_2, :v6) == Base.PARTITION_KIND_CONST +end end diff --git a/test/decls_ir.jl b/test/decls_ir.jl index 155754cd..9ad27bdf 100644 --- a/test/decls_ir.jl +++ b/test/decls_ir.jl @@ -53,7 +53,7 @@ end # Global declaration allowed in tail position global x #--------------------- -1 (global TestMod.x) +1 (call core.declare_global TestMod :x false) 2 latestworld 3 (return core.nothing) @@ -63,7 +63,7 @@ begin global x end #--------------------- -1 (global TestMod.x) +1 (call core.declare_global TestMod :x false) 2 latestworld 3 (return core.nothing) @@ -92,7 +92,7 @@ y = global x const xx = 10 #--------------------- 1 10 -2 (constdecl TestMod.xx %₁) +2 (call core.declare_const TestMod :xx %₁) 3 latestworld 4 (return %₁) @@ -110,26 +110,34 @@ const xx::T = 10 8 (call top.convert %₁ %₇) 9 (= slot₁/tmp (call core.typeassert %₈ %₁)) 10 slot₁/tmp -11 (constdecl TestMod.xx %₁₀) +11 (call core.declare_const TestMod :xx %₁₀) 12 latestworld 13 (return %₁₀) ######################################## -# Error: Const tuple +# Const tuple const xxx,xxxx,xxxxx = 10,20,30 #--------------------- -LoweringError: -const xxx,xxxx,xxxxx = 10,20,30 -# └─────────────┘ ── Lowering TODO: `const` tuple assignment desugaring +1 10 +2 (call core.declare_const TestMod :xxx %₁) +3 latestworld +4 20 +5 (call core.declare_const TestMod :xxxx %₄) +6 latestworld +7 30 +8 (call core.declare_const TestMod :xxxxx %₇) +9 latestworld +10 (call core.tuple 10 20 30) +11 (return %₁₀) ######################################## # Const in chain: only first is const const c0 = v0 = v1 = 123 #--------------------- 1 123 -2 (constdecl TestMod.c0 %₁) +2 (call core.declare_const TestMod :c0 %₁) 3 latestworld -4 (globaldecl TestMod.v0) +4 (call core.declare_global TestMod :v0 true) 5 latestworld 6 (call core.get_binding_type TestMod :v0) 7 (= slot₁/tmp %₁) @@ -141,7 +149,7 @@ const c0 = v0 = v1 = 123 13 (= slot₁/tmp (call top.convert %₆ %₁₂)) 14 slot₁/tmp 15 (call core.setglobal! TestMod :v0 %₁₄) -16 (globaldecl TestMod.v1) +16 (call core.declare_global TestMod :v1 true) 17 latestworld 18 (call core.get_binding_type TestMod :v1) 19 (= slot₂/tmp %₁) @@ -159,7 +167,7 @@ const c0 = v0 = v1 = 123 # Global assignment xx = 10 #--------------------- -1 (globaldecl TestMod.xx) +1 (call core.declare_global TestMod :xx true) 2 latestworld 3 (call core.get_binding_type TestMod :xx) 4 (= slot₁/tmp 10) @@ -177,23 +185,24 @@ xx = 10 # Typed global assignment global xx::T = 10 #--------------------- -1 (globaldecl TestMod.xx TestMod.T) +1 (call core.declare_global TestMod :xx false) 2 latestworld -3 (global TestMod.xx) -4 latestworld -5 (globaldecl TestMod.xx) -6 latestworld -7 (call core.get_binding_type TestMod :xx) -8 (= slot₁/tmp 10) -9 slot₁/tmp -10 (call core.isa %₉ %₇) -11 (gotoifnot %₁₀ label₁₃) -12 (goto label₁₅) -13 slot₁/tmp -14 (= slot₁/tmp (call top.convert %₇ %₁₃)) -15 slot₁/tmp -16 (call core.setglobal! TestMod :xx %₁₅) -17 (return 10) +3 TestMod.T +4 (call core.declare_global TestMod :xx true %₃) +5 latestworld +6 (call core.declare_global TestMod :xx true) +7 latestworld +8 (call core.get_binding_type TestMod :xx) +9 (= slot₁/tmp 10) +10 slot₁/tmp +11 (call core.isa %₁₀ %₈) +12 (gotoifnot %₁₁ label₁₄) +13 (goto label₁₆) +14 slot₁/tmp +15 (= slot₁/tmp (call top.convert %₈ %₁₄)) +16 slot₁/tmp +17 (call core.setglobal! TestMod :xx %₁₆) +18 (return 10) ######################################## # Error: x declared twice @@ -206,7 +215,7 @@ LoweringError: begin local x::T = 1 local x::S = 1 -# └──┘ ── multiple type declarations found for `x` +# └───────┘ ── multiple type declarations found for `x` end ######################################## @@ -286,6 +295,6 @@ end LoweringError: function f() global x::Int = 1 -# └────┘ ── type declarations for global variables must be at top level, not inside a function +# └─────────┘ ── type declarations for global variables must be at top level, not inside a function end diff --git a/test/misc_ir.jl b/test/misc_ir.jl index a93e5ac1..345b8676 100644 --- a/test/misc_ir.jl +++ b/test/misc_ir.jl @@ -513,3 +513,32 @@ include("hi.jl") 3 latestworld 4 (return %₂) +######################################## +# Const function assignment syntax (legacy) +const f(x::Int)::Int = x+1 +#--------------------- +1 TestMod.f +2 TestMod.x +3 TestMod.Int +4 (call core.typeassert %₂ %₃) +5 (call %₁ %₄) +6 TestMod.Int +7 (call core.typeassert %₅ %₆) +8 (return %₇) + +######################################## +# Error: Destructuring assignment method definitions (broken, legacy) +f(x)::Int, g() = [1.0, 2.0] +#--------------------- +LoweringError: +f(x)::Int, g() = [1.0, 2.0] +└──┘ ── invalid assignment location + +######################################## +# Error: Destructuring assignment typdef, variable, and function (broken, legacy) +T{U}, (x::Float64, g()) = [Bool, (1, 2)] +#--------------------- +LoweringError: +T{U}, (x::Float64, g()) = [Bool, (1, 2)] +# └─┘ ── invalid assignment location + diff --git a/test/scopes_ir.jl b/test/scopes_ir.jl index da1f3529..84258ef2 100644 --- a/test/scopes_ir.jl +++ b/test/scopes_ir.jl @@ -166,7 +166,7 @@ begin @islocal(x) end #--------------------- -1 (global TestMod.x) +1 (call core.declare_global TestMod :x false) 2 latestworld 3 (return false) @@ -179,7 +179,7 @@ begin end #--------------------- 1 (newvar slot₁/y) -2 (global TestMod.x) +2 (call core.declare_global TestMod :x false) 3 latestworld 4 (call core.apply_type top.Dict core.Symbol core.Any) 5 (call %₄) @@ -429,3 +429,105 @@ end 3 (call core.isdefinedglobal TestMod :y false) 4 (return %₃) +######################################## +# Global function defined inside let (let over lambda) +let x = 1 + global f(y) = x = y + global g() = x +end +#--------------------- +1 1 +2 (= slot₁/x (call core.Box)) +3 slot₁/x +4 (call core.setfield! %₃ :contents %₁) +5 (call core.declare_global TestMod :f false) +6 latestworld +7 (method TestMod.f) +8 latestworld +9 TestMod.f +10 (call core.Typeof %₉) +11 (call core.svec %₁₀ core.Any) +12 (call core.svec) +13 SourceLocation::2:12 +14 (call core.svec %₁₁ %₁₂ %₁₃) +15 --- code_info + slots: [slot₁/#self#(!read) slot₂/y] + 1 slot₂/y + 2 (captured_local 1) + 3 (call core.setfield! %₂ :contents %₁) + 4 (return %₁) +16 slot₁/x +17 (call core.svec %₁₆) +18 (call JuliaLowering.replace_captured_locals! %₁₅ %₁₇) +19 --- method core.nothing %₁₄ %₁₈ +20 latestworld +21 (call core.declare_global TestMod :g false) +22 latestworld +23 (method TestMod.g) +24 latestworld +25 TestMod.g +26 (call core.Typeof %₂₅) +27 (call core.svec %₂₆) +28 (call core.svec) +29 SourceLocation::3:12 +30 (call core.svec %₂₇ %₂₈ %₂₉) +31 --- code_info + slots: [slot₁/#self#(!read) slot₂/x(!read)] + 1 (captured_local 1) + 2 (call core.isdefined %₁ :contents) + 3 (gotoifnot %₂ label₅) + 4 (goto label₇) + 5 (newvar slot₂/x) + 6 slot₂/x + 7 (call core.getfield %₁ :contents) + 8 (return %₇) +32 slot₁/x +33 (call core.svec %₃₂) +34 (call JuliaLowering.replace_captured_locals! %₃₁ %₃₃) +35 --- method core.nothing %₃₀ %₃₄ +36 latestworld +37 TestMod.g +38 (return %₃₇) + +######################################## +# Modify assignment operator on closure variable +let x = 1 + global f() = x += 1 +end +#--------------------- +1 1 +2 (= slot₁/x (call core.Box)) +3 slot₁/x +4 (call core.setfield! %₃ :contents %₁) +5 (call core.declare_global TestMod :f false) +6 latestworld +7 (method TestMod.f) +8 latestworld +9 TestMod.f +10 (call core.Typeof %₉) +11 (call core.svec %₁₀) +12 (call core.svec) +13 SourceLocation::2:12 +14 (call core.svec %₁₁ %₁₂ %₁₃) +15 --- code_info + slots: [slot₁/#self#(!read) slot₂/x(!read)] + 1 TestMod.+ + 2 (captured_local 1) + 3 (call core.isdefined %₂ :contents) + 4 (gotoifnot %₃ label₆) + 5 (goto label₈) + 6 (newvar slot₂/x) + 7 slot₂/x + 8 (call core.getfield %₂ :contents) + 9 (call %₁ %₈ 1) + 10 (captured_local 1) + 11 (call core.setfield! %₁₀ :contents %₉) + 12 (return %₉) +16 slot₁/x +17 (call core.svec %₁₆) +18 (call JuliaLowering.replace_captured_locals! %₁₅ %₁₇) +19 --- method core.nothing %₁₄ %₁₈ +20 latestworld +21 TestMod.f +22 (return %₂₁) + diff --git a/test/typedefs_ir.jl b/test/typedefs_ir.jl index 1fcc96fc..b1a9f920 100644 --- a/test/typedefs_ir.jl +++ b/test/typedefs_ir.jl @@ -193,7 +193,7 @@ abstract type A end 4 (call core._setsuper! %₂ core.Any) 5 slot₁/A 6 (call core._typebody! false %₅) -7 (global TestMod.A) +7 (call core.declare_global TestMod :A false) 8 latestworld 9 (call core.isdefinedglobal TestMod :A false) 10 (gotoifnot %₉ label₁₅) @@ -201,7 +201,7 @@ abstract type A end 12 (call core._equiv_typedef %₁₁ %₂) 13 (gotoifnot %₁₂ label₁₅) 14 (goto label₁₇) -15 (constdecl TestMod.A %₂) +15 (call core.declare_const TestMod :A %₂) 16 latestworld 17 (return core.nothing) @@ -216,7 +216,7 @@ abstract type A <: B end 5 (call core._setsuper! %₂ %₄) 6 slot₁/A 7 (call core._typebody! false %₆) -8 (global TestMod.A) +8 (call core.declare_global TestMod :A false) 9 latestworld 10 (call core.isdefinedglobal TestMod :A false) 11 (gotoifnot %₁₀ label₁₆) @@ -224,7 +224,7 @@ abstract type A <: B end 13 (call core._equiv_typedef %₁₂ %₂) 14 (gotoifnot %₁₃ label₁₆) 15 (goto label₁₈) -16 (constdecl TestMod.A %₂) +16 (call core.declare_const TestMod :A %₂) 17 latestworld 18 (return core.nothing) @@ -243,7 +243,7 @@ abstract type A{X, Y <: X} end 9 (call core._setsuper! %₇ core.Any) 10 slot₁/A 11 (call core._typebody! false %₁₀) -12 (global TestMod.A) +12 (call core.declare_global TestMod :A false) 13 latestworld 14 (call core.isdefinedglobal TestMod :A false) 15 (gotoifnot %₁₄ label₂₀) @@ -251,7 +251,7 @@ abstract type A{X, Y <: X} end 17 (call core._equiv_typedef %₁₆ %₇) 18 (gotoifnot %₁₇ label₂₀) 19 (goto label₂₂) -20 (constdecl TestMod.A %₇) +20 (call core.declare_const TestMod :A %₇) 21 latestworld 22 (return core.nothing) @@ -301,7 +301,7 @@ primitive type P 8 end 4 (call core._setsuper! %₂ core.Any) 5 slot₁/P 6 (call core._typebody! false %₅) -7 (global TestMod.P) +7 (call core.declare_global TestMod :P false) 8 latestworld 9 (call core.isdefinedglobal TestMod :P false) 10 (gotoifnot %₉ label₁₅) @@ -309,7 +309,7 @@ primitive type P 8 end 12 (call core._equiv_typedef %₁₁ %₂) 13 (gotoifnot %₁₂ label₁₅) 14 (goto label₁₇) -15 (constdecl TestMod.P %₂) +15 (call core.declare_const TestMod :P %₂) 16 latestworld 17 (return core.nothing) @@ -328,7 +328,7 @@ primitive type P{X,Y} <: Z 32 end 9 (call core._setsuper! %₆ %₈) 10 slot₁/P 11 (call core._typebody! false %₁₀) -12 (global TestMod.P) +12 (call core.declare_global TestMod :P false) 13 latestworld 14 (call core.isdefinedglobal TestMod :P false) 15 (gotoifnot %₁₄ label₂₀) @@ -336,7 +336,7 @@ primitive type P{X,Y} <: Z 32 end 17 (call core._equiv_typedef %₁₆ %₆) 18 (gotoifnot %₁₇ label₂₀) 19 (goto label₂₂) -20 (constdecl TestMod.P %₆) +20 (call core.declare_const TestMod :P %₆) 21 latestworld 22 (return core.nothing) @@ -352,7 +352,7 @@ primitive type P P_nbits() end 6 (call core._setsuper! %₄ core.Any) 7 slot₁/P 8 (call core._typebody! false %₇) -9 (global TestMod.P) +9 (call core.declare_global TestMod :P false) 10 latestworld 11 (call core.isdefinedglobal TestMod :P false) 12 (gotoifnot %₁₁ label₁₇) @@ -360,7 +360,7 @@ primitive type P P_nbits() end 14 (call core._equiv_typedef %₁₃ %₄) 15 (gotoifnot %₁₄ label₁₇) 16 (goto label₁₉) -17 (constdecl TestMod.P %₄) +17 (call core.declare_const TestMod :P %₄) 18 latestworld 19 (return core.nothing) @@ -369,7 +369,7 @@ primitive type P P_nbits() end struct X end #--------------------- -1 (global TestMod.X) +1 (call core.declare_global TestMod :X false) 2 latestworld 3 (call core.svec) 4 (call core.svec) @@ -393,7 +393,7 @@ end 22 (gotoifnot %₁₅ label₂₃) 23 (call core.svec) 24 (call core._typebody! %₂₁ %₆ %₂₃) -25 (constdecl TestMod.X %₂₄) +25 (call core.declare_const TestMod :X %₂₄) 26 latestworld 27 TestMod.X 28 (call core.apply_type core.Type %₂₇) @@ -415,7 +415,7 @@ struct X X() = new() end #--------------------- -1 (global TestMod.X) +1 (call core.declare_global TestMod :X false) 2 latestworld 3 (call core.svec) 4 (call core.svec) @@ -439,7 +439,7 @@ end 22 (gotoifnot %₁₅ label₂₃) 23 (call core.svec) 24 (call core._typebody! %₂₁ %₆ %₂₃) -25 (constdecl TestMod.X %₂₄) +25 (call core.declare_const TestMod :X %₂₄) 26 latestworld 27 TestMod.X 28 (call core.apply_type core.Type %₂₇) @@ -463,7 +463,7 @@ struct X c end #--------------------- -1 (global TestMod.X) +1 (call core.declare_global TestMod :X false) 2 latestworld 3 (call core.svec) 4 (call core.svec :a :b :c) @@ -488,7 +488,7 @@ end 23 TestMod.T 24 (call core.svec core.Any %₂₃ core.Any) 25 (call core._typebody! %₂₁ %₆ %₂₄) -26 (constdecl TestMod.X %₂₅) +26 (call core.declare_const TestMod :X %₂₅) 27 latestworld 28 TestMod.T 29 (call core.=== core.Any %₂₈) @@ -535,7 +535,7 @@ end struct X{U, S <: V <: T} <: Z end #--------------------- -1 (global TestMod.X) +1 (call core.declare_global TestMod :X false) 2 latestworld 3 (= slot₂/U (call core.TypeVar :U)) 4 TestMod.S @@ -576,7 +576,7 @@ end 39 (= slot₃/V (call core.getfield %₃₈ 1)) 40 (call core.svec) 41 (call core._typebody! %₂₈ %₁₂ %₄₀) -42 (constdecl TestMod.X %₄₁) +42 (call core.declare_const TestMod :X %₄₁) 43 latestworld 44 slot₂/U 45 slot₃/V @@ -606,7 +606,7 @@ struct X const @atomic c end #--------------------- -1 (global TestMod.X) +1 (call core.declare_global TestMod :X false) 2 latestworld 3 (call core.svec) 4 (call core.svec :a :b :c) @@ -630,7 +630,7 @@ end 22 (gotoifnot %₁₅ label₂₃) 23 (call core.svec core.Any core.Any core.Any) 24 (call core._typebody! %₂₁ %₆ %₂₃) -25 (constdecl TestMod.X %₂₄) +25 (call core.declare_const TestMod :X %₂₄) 26 latestworld 27 TestMod.X 28 (call core.apply_type core.Type %₂₇) @@ -658,7 +658,7 @@ struct X b end #--------------------- -1 (global TestMod.X) +1 (call core.declare_global TestMod :X false) 2 latestworld 3 (call core.svec) 4 (call core.svec :a :b) @@ -682,7 +682,7 @@ end 22 (gotoifnot %₁₅ label₂₃) 23 (call core.svec core.Any core.Any) 24 (call core._typebody! %₂₁ %₆ %₂₃) -25 (constdecl TestMod.X %₂₄) +25 (call core.declare_const TestMod :X %₂₄) 26 latestworld 27 TestMod.X 28 (call core.apply_type core.Type %₂₇) @@ -713,7 +713,7 @@ struct X{U} x::U end #--------------------- -1 (global TestMod.X) +1 (call core.declare_global TestMod :X false) 2 latestworld 3 (= slot₁/U (call core.TypeVar :U)) 4 slot₁/U @@ -745,7 +745,7 @@ end 30 slot₁/U 31 (call core.svec %₃₀) 32 (call core._typebody! %₂₃ %₈ %₃₁) -33 (constdecl TestMod.X %₃₂) +33 (call core.declare_const TestMod :X %₃₂) 34 latestworld 35 slot₁/U 36 TestMod.X @@ -797,7 +797,7 @@ struct X{T, S <: Vector{T}} v::Vector{S} end #--------------------- -1 (global TestMod.X) +1 (call core.declare_global TestMod :X false) 2 latestworld 3 (= slot₃/T (call core.TypeVar :T)) 4 TestMod.Vector @@ -841,7 +841,7 @@ end 42 (call core.apply_type %₄₀ %₄₁) 43 (call core.svec %₄₂) 44 (call core._typebody! %₂₈ %₁₃ %₄₃) -45 (constdecl TestMod.X %₄₄) +45 (call core.declare_const TestMod :X %₄₄) 46 latestworld 47 slot₃/T 48 slot₂/S @@ -908,7 +908,7 @@ struct X end #--------------------- 1 (= slot₂/f (call core.Box)) -2 (global TestMod.X) +2 (call core.declare_global TestMod :X false) 3 latestworld 4 (call core.svec) 5 (call core.svec :x) @@ -932,7 +932,7 @@ end 23 (gotoifnot %₁₆ label₂₄) 24 (call core.svec core.Any) 25 (call core._typebody! %₂₂ %₇ %₂₄) -26 (constdecl TestMod.X %₂₅) +26 (call core.declare_const TestMod :X %₂₅) 27 latestworld 28 (call core.svec) 29 (call core.svec) @@ -1037,7 +1037,7 @@ struct X{S,T} end #--------------------- 1 (newvar slot₅/f) -2 (global TestMod.X) +2 (call core.declare_global TestMod :X false) 3 latestworld 4 (= slot₂/S (call core.TypeVar :S)) 5 (= slot₃/T (call core.TypeVar :T)) @@ -1075,7 +1075,7 @@ end 37 (= slot₃/T (call core.getfield %₃₆ 1)) 38 (call core.svec core.Any) 39 (call core._typebody! %₂₆ %₁₁ %₃₈) -40 (constdecl TestMod.X %₃₉) +40 (call core.declare_const TestMod :X %₃₉) 41 latestworld 42 TestMod.X 43 TestMod.A @@ -1142,7 +1142,7 @@ struct X X(xs) = new(xs...) end #--------------------- -1 (global TestMod.X) +1 (call core.declare_global TestMod :X false) 2 latestworld 3 (call core.svec) 4 (call core.svec :x :y) @@ -1166,7 +1166,7 @@ end 22 (gotoifnot %₁₅ label₂₃) 23 (call core.svec core.Any core.Any) 24 (call core._typebody! %₂₁ %₆ %₂₃) -25 (constdecl TestMod.X %₂₄) +25 (call core.declare_const TestMod :X %₂₄) 26 latestworld 27 TestMod.X 28 (call core.apply_type core.Type %₂₇) @@ -1191,7 +1191,7 @@ struct X{T} X{T}(xs) where {T} = new(xs...) end #--------------------- -1 (global TestMod.X) +1 (call core.declare_global TestMod :X false) 2 latestworld 3 (= slot₁/T (call core.TypeVar :T)) 4 slot₁/T @@ -1224,7 +1224,7 @@ end 31 TestMod.A 32 (call core.svec %₃₀ %₃₁) 33 (call core._typebody! %₂₃ %₈ %₃₂) -34 (constdecl TestMod.X %₃₃) +34 (call core.declare_const TestMod :X %₃₃) 35 latestworld 36 (= slot₃/T (call core.TypeVar :T)) 37 TestMod.X diff --git a/test/utils.jl b/test/utils.jl index 4507f93a..9c27117f 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -25,7 +25,8 @@ function _ast_test_graph() ensure_attributes!(graph, kind=Kind, syntax_flags=UInt16, source=Union{SourceRef,NodeId,Tuple,LineNumberNode}, - var_id=Int, value=Any, name_val=String, is_toplevel_thunk=Bool) + var_id=Int, value=Any, name_val=String, is_toplevel_thunk=Bool, + toplevel_pure=Bool) end function _source_node(graph, src)