Skip to content

Commit ad5d6d5

Browse files
committed
lowering: preserve line numbers over julia-expand-macroscope pass
This is to preserve the line number. But we make slightly more changes than strictly necessary to prepare for future improvements in this area.
1 parent aea56a9 commit ad5d6d5

File tree

17 files changed

+195
-85
lines changed

17 files changed

+195
-85
lines changed

base/boot.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -533,11 +533,10 @@ import Core: CodeInfo, MethodInstance, CodeInstance, GotoNode, GotoIfNot, Return
533533
end # module IR
534534

535535
# docsystem basics
536-
const unescape = Symbol("hygienic-scope")
537536
macro doc(x...)
538537
docex = atdoc(__source__, __module__, x...)
539538
isa(docex, Expr) && docex.head === :escape && return docex
540-
return Expr(:escape, Expr(unescape, docex, typeof(atdoc).name.module))
539+
return Expr(:escape, Expr(:var"hygienic-scope", docex, typeof(atdoc).name.module, __source__))
541540
end
542541
macro __doc__(x)
543542
return Expr(:escape, Expr(:block, Expr(:meta, :doc), x))

base/docs/Docs.jl

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -286,12 +286,26 @@ catdoc(xs...) = vcat(xs...)
286286
const keywords = Dict{Symbol, DocStr}()
287287

288288
function unblock(@nospecialize ex)
289+
while isexpr(ex, :var"hygienic-scope")
290+
isexpr(ex.args[1], :escape) || break
291+
ex = ex.args[1].args[1]
292+
end
289293
isexpr(ex, :block) || return ex
290294
exs = filter(ex -> !(isa(ex, LineNumberNode) || isexpr(ex, :line)), ex.args)
291295
length(exs) == 1 || return ex
292296
return unblock(exs[1])
293297
end
294298

299+
# peek through ex to figure out what kind of expression it may eventually act like
300+
# but ignoring scopes and line numbers
301+
function unescape(@nospecialize ex)
302+
ex = unblock(ex)
303+
while isexpr(ex, :escape) || isexpr(ex, :var"hygienic-scope")
304+
ex = unblock(ex.args[1])
305+
end
306+
return ex
307+
end
308+
295309
uncurly(@nospecialize ex) = isexpr(ex, :curly) ? ex.args[1] : ex
296310

297311
namify(@nospecialize x) = astname(x, isexpr(x, :macro))::Union{Symbol,Expr,GlobalRef}
@@ -351,18 +365,19 @@ function metadata(__source__, __module__, expr, ismodule)
351365
fields = P[]
352366
last_docstr = nothing
353367
for each in (expr.args[3]::Expr).args
354-
if isa(each, Symbol) || isexpr(each, :(::))
368+
eachex = unescape(each)
369+
if isa(eachex, Symbol) || isexpr(eachex, :(::))
355370
# a field declaration
356371
if last_docstr !== nothing
357-
push!(fields, P(namify(each::Union{Symbol,Expr}), last_docstr))
372+
push!(fields, P(namify(eachex::Union{Symbol,Expr}), last_docstr))
358373
last_docstr = nothing
359374
end
360-
elseif isexpr(each, :function) || isexpr(each, :(=))
375+
elseif isexpr(eachex, :function) || isexpr(eachex, :(=))
361376
break
362-
elseif isa(each, String) || isexpr(each, :string) || isexpr(each, :call) ||
363-
(isexpr(each, :macrocall) && each.args[1] === Symbol("@doc_str"))
377+
elseif isa(eachex, String) || isexpr(eachex, :string) || isexpr(eachex, :call) ||
378+
(isexpr(eachex, :macrocall) && eachex.args[1] === Symbol("@doc_str"))
364379
# forms that might be doc strings
365-
last_docstr = each::Union{String,Expr}
380+
last_docstr = each
366381
end
367382
end
368383
dict = :($(Dict{Symbol,Any})($([(:($(P)($(quot(f)), $d)))::Expr for (f, d) in fields]...)))
@@ -627,8 +642,9 @@ function loaddocs(docs::Vector{Core.SimpleVector})
627642
for (mod, ex, str, file, line) in docs
628643
data = Dict{Symbol,Any}(:path => string(file), :linenumber => line)
629644
doc = docstr(str, data)
630-
docstring = docm(LineNumberNode(line, file), mod, doc, ex, false) # expand the real @doc macro now
631-
Core.eval(mod, Expr(Core.unescape, docstring, Docs))
645+
lno = LineNumberNode(line, file)
646+
docstring = docm(lno, mod, doc, ex, false) # expand the real @doc macro now
647+
Core.eval(mod, Expr(:var"hygienic-scope", docstring, Docs, lno))
632648
end
633649
empty!(docs)
634650
nothing

base/osutils.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ macro static(ex)
1616
@label loop
1717
hd = ex.head
1818
if hd (:if, :elseif, :&&, :||)
19-
cond = Core.eval(__module__, ex.args[1])
19+
cond = Core.eval(__module__, ex.args[1])::Bool
2020
if xor(cond, hd === :||)
2121
return esc(ex.args[2])
2222
elseif length(ex.args) == 3

base/threadcall.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ macro threadcall(f, rettype, argtypes, argvals...)
4747
push!(body, :(return Int(Core.sizeof($rettype))))
4848

4949
# return code to generate wrapper function and send work request thread queue
50-
wrapper = Expr(Symbol("hygienic-scope"), wrapper, @__MODULE__)
50+
wrapper = Expr(:var"hygienic-scope", wrapper, @__MODULE__, __source__)
5151
return :(let fun_ptr = @cfunction($wrapper, Int, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}))
5252
# use cglobal to look up the function on the calling thread
5353
do_threadcall(fun_ptr, cglobal($f), $rettype, Any[$(argtypes...)], Any[$(argvals...)])

base/util.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ macro kwdef(expr)
604604
kwdefs = nothing
605605
end
606606
return quote
607-
Base.@__doc__ $(esc(expr))
607+
$(esc(:($Base.@__doc__ $expr)))
608608
$kwdefs
609609
end
610610
end

src/ast.c

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,8 @@ static jl_value_t *scm_to_julia(fl_context_t *fl_ctx, value_t e, jl_module_t *mo
436436
}
437437
JL_CATCH {
438438
// if expression cannot be converted, replace with error expr
439+
//jl_(jl_current_exception());
440+
//jlbacktrace();
439441
jl_expr_t *ex = jl_exprn(jl_error_sym, 1);
440442
v = (jl_value_t*)ex;
441443
jl_array_ptr_set(ex->args, 0, jl_cstr_to_string("invalid AST"));
@@ -1000,7 +1002,59 @@ int jl_has_meta(jl_array_t *body, jl_sym_t *sym) JL_NOTSAFEPOINT
10001002
return 0;
10011003
}
10021004

1003-
static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule, jl_module_t **ctx, size_t world, int throw_load_error)
1005+
// Utility function to return whether `e` is any of the special AST types or
1006+
// will always evaluate to itself exactly unchanged. This corresponds to
1007+
// `is_self_quoting` in Core.Compiler utilities.
1008+
int jl_is_ast_node(jl_value_t *e) JL_NOTSAFEPOINT
1009+
{
1010+
return jl_is_newvarnode(e)
1011+
|| jl_is_code_info(e)
1012+
|| jl_is_linenode(e)
1013+
|| jl_is_gotonode(e)
1014+
|| jl_is_gotoifnot(e)
1015+
|| jl_is_returnnode(e)
1016+
|| jl_is_ssavalue(e)
1017+
|| jl_is_slotnumber(e)
1018+
|| jl_is_argument(e)
1019+
|| jl_is_quotenode(e)
1020+
|| jl_is_globalref(e)
1021+
|| jl_is_symbol(e)
1022+
|| jl_is_pinode(e)
1023+
|| jl_is_phinode(e)
1024+
|| jl_is_phicnode(e)
1025+
|| jl_is_upsilonnode(e)
1026+
|| jl_is_expr(e);
1027+
}
1028+
1029+
static int is_self_quoting_expr(jl_expr_t *e) JL_NOTSAFEPOINT
1030+
{
1031+
return (e->head == jl_inert_sym ||
1032+
e->head == jl_core_sym ||
1033+
e->head == jl_line_sym ||
1034+
e->head == jl_lineinfo_sym ||
1035+
e->head == jl_meta_sym ||
1036+
e->head == jl_boundscheck_sym ||
1037+
e->head == jl_inline_sym ||
1038+
e->head == jl_noinline_sym);
1039+
}
1040+
1041+
// any AST, except those that cannot contain symbols
1042+
// and have no side effects
1043+
int need_esc_node(jl_value_t *e) JL_NOTSAFEPOINT
1044+
{
1045+
if (jl_is_linenode(e)
1046+
|| jl_is_ssavalue(e)
1047+
|| jl_is_slotnumber(e)
1048+
|| jl_is_argument(e)
1049+
|| jl_is_quotenode(e))
1050+
return 0;
1051+
if (jl_is_expr(e))
1052+
return !is_self_quoting_expr((jl_expr_t*)e);
1053+
// note: jl_is_globalref(e) is not included here, since we care a little about about having a line number for it
1054+
return jl_is_ast_node(e);
1055+
}
1056+
1057+
static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule, jl_module_t **ctx, jl_value_t **lineinfo, size_t world, int throw_load_error)
10041058
{
10051059
jl_task_t *ct = jl_current_task;
10061060
JL_TIMING(MACRO_INVOCATION, MACRO_INVOCATION);
@@ -1012,10 +1066,9 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule
10121066
margs[0] = jl_array_ptr_ref(args, 0);
10131067
// __source__ argument
10141068
jl_value_t *lno = jl_array_ptr_ref(args, 1);
1069+
if (!jl_is_linenode(lno))
1070+
lno = jl_new_struct(jl_linenumbernode_type, jl_box_long(0), jl_nothing);
10151071
margs[1] = lno;
1016-
if (!jl_is_linenode(lno)) {
1017-
margs[1] = jl_new_struct(jl_linenumbernode_type, jl_box_long(0), jl_nothing);
1018-
}
10191072
margs[2] = (jl_value_t*)inmodule;
10201073
for (i = 3; i < nargs; i++)
10211074
margs[i] = jl_array_ptr_ref(args, i - 1);
@@ -1054,6 +1107,7 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule
10541107
}
10551108
}
10561109
ct->world_age = last_age;
1110+
*lineinfo = margs[1];
10571111
JL_GC_POP();
10581112
return result;
10591113
}
@@ -1076,36 +1130,47 @@ static jl_value_t *jl_expand_macros(jl_value_t *expr, jl_module_t *inmodule, str
10761130
JL_GC_POP();
10771131
return expr;
10781132
}
1079-
if (e->head == jl_hygienicscope_sym && jl_expr_nargs(e) == 2) {
1133+
if (e->head == jl_hygienicscope_sym && jl_expr_nargs(e) >= 2) {
10801134
struct macroctx_stack newctx;
10811135
newctx.m = (jl_module_t*)jl_exprarg(e, 1);
10821136
JL_TYPECHK(hygienic-scope, module, (jl_value_t*)newctx.m);
10831137
newctx.parent = macroctx;
10841138
jl_value_t *a = jl_exprarg(e, 0);
10851139
jl_value_t *a2 = jl_expand_macros(a, inmodule, &newctx, onelevel, world, throw_load_error);
1086-
if (a != a2)
1140+
if (jl_is_expr(a2) && ((jl_expr_t*)a2)->head == jl_escape_sym && !need_esc_node(jl_exprarg(a2, 0)))
1141+
expr = jl_exprarg(a2, 0);
1142+
else if (!need_esc_node(a2))
1143+
expr = a2;
1144+
else if (a != a2)
10871145
jl_array_ptr_set(e->args, 0, a2);
10881146
return expr;
10891147
}
10901148
if (e->head == jl_macrocall_sym) {
10911149
struct macroctx_stack newctx;
10921150
newctx.m = macroctx ? macroctx->m : inmodule;
10931151
newctx.parent = macroctx;
1094-
jl_value_t *result = jl_invoke_julia_macro(e->args, inmodule, &newctx.m, world, throw_load_error);
1152+
jl_value_t *lineinfo = NULL;
1153+
jl_value_t *result = jl_invoke_julia_macro(e->args, inmodule, &newctx.m, &lineinfo, world, throw_load_error);
1154+
if (!need_esc_node(result))
1155+
return result;
10951156
jl_value_t *wrap = NULL;
1096-
JL_GC_PUSH3(&result, &wrap, &newctx.m);
1157+
JL_GC_PUSH4(&result, &wrap, &newctx.m, &lineinfo);
10971158
// copy and wrap the result in `(hygienic-scope ,result ,newctx)
10981159
if (jl_is_expr(result) && ((jl_expr_t*)result)->head == jl_escape_sym)
10991160
result = jl_exprarg(result, 0);
11001161
else
1101-
wrap = (jl_value_t*)jl_exprn(jl_hygienicscope_sym, 2);
1162+
wrap = (jl_value_t*)jl_exprn(jl_hygienicscope_sym, 3);
11021163
result = jl_copy_ast(result);
11031164
if (!onelevel)
11041165
result = jl_expand_macros(result, inmodule, wrap ? &newctx : macroctx, onelevel, world, throw_load_error);
1105-
if (wrap) {
1166+
if (wrap && need_esc_node(result)) {
11061167
jl_exprargset(wrap, 0, result);
11071168
jl_exprargset(wrap, 1, newctx.m);
1108-
result = wrap;
1169+
jl_exprargset(wrap, 2, lineinfo);
1170+
if (jl_is_expr(result) && ((jl_expr_t*)result)->head == jl_escape_sym)
1171+
result = jl_exprarg(result, 0);
1172+
else
1173+
result = wrap;
11091174
}
11101175
JL_GC_POP();
11111176
return result;

src/ast.scm

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -479,12 +479,13 @@
479479
(define (eq-sym? a b)
480480
(or (eq? a b) (and (ssavalue? a) (ssavalue? b) (eqv? (cdr a) (cdr b)))))
481481

482-
(define (blockify e)
482+
(define (blockify e (lno #f))
483+
(set! lno (if lno (list lno) '()))
483484
(if (and (pair? e) (eq? (car e) 'block))
484485
(if (null? (cdr e))
485-
`(block (null))
486-
e)
487-
`(block ,e)))
486+
`(block ,@lno (null))
487+
(if (null? lno) e `(block ,@lno ,@(cdr e))))
488+
`(block ,@lno ,e)))
488489

489490
(define (make-var-info name) (list name '(core Any) 0))
490491
(define vinfo:name car)

src/jlfrontend.scm

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,38 @@
9393

9494
;; lowering entry points
9595

96+
; find the first line number in this expression, before we might eliminate them
97+
(define (first-lineno blk)
98+
(cond ((not (pair? blk)) #f)
99+
((eq? (car blk) 'line) blk)
100+
((and (eq? (car blk) 'hygienic-scope) (pair? (cdddr blk)) (pair? (cadddr blk)) (eq? (car (cadddr blk)) 'line))
101+
(cadddr blk))
102+
((memq (car blk) '(escape hygienic-scope))
103+
(first-lineno (cadr blk)))
104+
((memq (car blk) '(toplevel block))
105+
(let loop ((xs (cdr blk)))
106+
(and (pair? xs)
107+
(let ((elt (first-lineno (car xs))))
108+
(or elt (loop (cdr xs)))))))
109+
(else #f)))
110+
96111
;; return a lambda expression representing a thunk for a top-level expression
97112
;; note: expansion of stuff inside module is delayed, so the contents obey
98113
;; toplevel expansion order (don't expand until stuff before is evaluated).
99114
(define (expand-toplevel-expr-- e file line)
100-
(let ((ex0 (julia-expand-macroscope e)))
115+
(let ((lno (first-lineno e))
116+
(ex0 (julia-expand-macroscope e)))
117+
(if (and lno (or (not (length= lno 3)) (not (atom? (caddr lno))))) (set! lno #f))
101118
(if (toplevel-only-expr? ex0)
102-
ex0
103-
(let* ((ex (julia-expand0 ex0 file line))
119+
(if (and (pair? e) (memq (car ex0) '(error incomplete)))
120+
ex0
121+
(if lno `(toplevel ,lno ,ex0) ex0))
122+
(let* ((linenode (if (and lno (or (= line 0) (eq? file 'none))) lno `(line ,line ,file)))
123+
(ex (julia-expand0 ex0 linenode))
104124
(th (julia-expand1
105125
`(lambda () ()
106126
(scope-block
107-
,(blockify ex)))
127+
,(blockify ex lno)))
108128
file line)))
109129
(if (and (null? (cdadr (caddr th)))
110130
(and (length= (lam:body th) 2)

src/julia-syntax.scm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5090,8 +5090,8 @@ f(x) = yt(x)
50905090

50915091
(define *current-desugar-loc* #f)
50925092

5093-
(define (julia-expand0 ex file line)
5094-
(with-bindings ((*current-desugar-loc* `(line ,line ,file)))
5093+
(define (julia-expand0 ex lno)
5094+
(with-bindings ((*current-desugar-loc* lno))
50955095
(trycatch (expand-forms ex)
50965096
(lambda (e)
50975097
(if (and (pair? e) (eq? (car e) 'error))
@@ -5106,4 +5106,4 @@ f(x) = yt(x)
51065106
(define (julia-expand ex (file 'none) (line 0))
51075107
(julia-expand1
51085108
(julia-expand0
5109-
(julia-expand-macroscope ex) file line) file line))
5109+
(julia-expand-macroscope ex) `(line ,line ,file)) file line))

src/macroexpand.scm

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,8 @@
448448
((hygienic-scope) ; TODO: move this lowering to resolve-scopes, instead of reimplementing it here badly
449449
(let ((parent-scope (cons (list env m) parent-scope))
450450
(body (cadr e))
451-
(m (caddr e)))
451+
(m (caddr e))
452+
(lno (cdddr e)))
452453
(resolve-expansion-vars-with-new-env body env m parent-scope inarg #t)))
453454
((tuple)
454455
(cons (car e)
@@ -574,7 +575,8 @@
574575
((eq? (car e) 'module) e)
575576
((eq? (car e) 'hygienic-scope)
576577
(let ((form (cadr e)) ;; form is the expression returned from expand-macros
577-
(modu (caddr e))) ;; m is the macro's def module
578+
(modu (caddr e)) ;; m is the macro's def module
579+
(lno (cdddr e))) ;; lno is (optionally) the line number node
578580
(resolve-expansion-vars form modu)))
579581
(else
580582
(map julia-expand-macroscopes- e))))
@@ -585,8 +587,9 @@
585587
((eq? (car e) 'hygienic-scope)
586588
(let ((parent-scope (list relabels parent-scope))
587589
(body (cadr e))
588-
(m (caddr e)))
589-
`(hygienic-scope ,(rename-symbolic-labels- (cadr e) (table) parent-scope) ,m)))
590+
(m (caddr e))
591+
(lno (cdddr e)))
592+
`(hygienic-scope ,(rename-symbolic-labels- (cadr e) (table) parent-scope) ,m ,@lno)))
590593
((and (eq? (car e) 'escape) (not (null? parent-scope)))
591594
`(escape ,(apply rename-symbolic-labels- (cadr e) parent-scope)))
592595
((or (eq? (car e) 'symbolicgoto) (eq? (car e) 'symboliclabel))

0 commit comments

Comments
 (0)