From c3db80d019d0359d03b31226d9b053a273d31df3 Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Wed, 5 Feb 2014 21:19:34 -0800 Subject: [PATCH 01/11] Add labels and gotos. (Fixes #101) --- base/base.jl | 8 ++++++++ base/exports.jl | 4 +++- src/alloc.c | 1 + src/interpreter.c | 28 +++++++++++++++++++++++++++- src/jltypes.c | 2 ++ src/julia-syntax.scm | 25 ++++++++++++++++++++----- src/julia.h | 1 + 7 files changed, 62 insertions(+), 7 deletions(-) diff --git a/base/base.jl b/base/base.jl index f68f5dce25b1b..29816e8b327e9 100644 --- a/base/base.jl +++ b/base/base.jl @@ -203,6 +203,14 @@ macro inbounds(blk) :(@boundscheck false $(esc(blk))) end +macro label(name::Symbol) + Expr(:symboliclabel, name) +end + +macro goto(name::Symbol) + Expr(:symbolicgoto, name) +end + # NOTE: Base shares Array with Core so we can add definitions to it Array{T,N}(::Type{T}, d::NTuple{N,Int}) = diff --git a/base/exports.jl b/base/exports.jl index 319b5958c2836..300fd789da6d0 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1343,4 +1343,6 @@ export @deprecate, @boundscheck, @inbounds, - @simd + @simd, + @label, + @goto diff --git a/src/alloc.c b/src/alloc.c index 1c59431460a5f..b6ed6f90ce83f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -77,6 +77,7 @@ jl_sym_t *line_sym; jl_sym_t *jl_incomplete_sym; // head symbols for each expression type jl_sym_t *goto_sym; jl_sym_t *goto_ifnot_sym; jl_sym_t *label_sym; jl_sym_t *return_sym; +jl_sym_t *symboliclabel_sym; jl_sym_t *symbolicgoto_sym; jl_sym_t *lambda_sym; jl_sym_t *assign_sym; jl_sym_t *null_sym; jl_sym_t *body_sym; jl_sym_t *macro_sym; jl_sym_t *method_sym; diff --git a/src/interpreter.c b/src/interpreter.c index 4587803921c9e..a4d80218e816d 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -1,4 +1,5 @@ #include +#include #include #include #ifdef _OS_WINDOWS_ @@ -470,6 +471,25 @@ static int label_idx(jl_value_t *tgt, jl_array_t *stmts) return j; } +static int symboliclabel_idx(jl_value_t *tgt, jl_array_t *stmts) +{ + size_t j; + jl_sym_t* stgt = (jl_sym_t*)tgt; + assert(jl_is_symbol(tgt)); + for(j=0; j < stmts->nrows; j++) { + jl_value_t *l = jl_cellref(stmts,j); + if (jl_is_expr(l) && ((jl_expr_t*)l)->head == symboliclabel_sym) { + assert(jl_array_len(((jl_expr_t*)l)->args)); + jl_value_t* labeltgt = jl_cellref(((jl_expr_t*)l)->args, 0); + if (jl_is_symbol(labeltgt) && (jl_sym_t*)labeltgt == stgt) { + break; + } + } + } + assert(j < stmts->nrows); + return j; +} + jl_value_t *jl_toplevel_eval_body(jl_array_t *stmts) { return eval_body(stmts, NULL, 0, 0, 1); @@ -489,7 +509,13 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, } if (jl_is_expr(stmt)) { jl_sym_t *head = ((jl_expr_t*)stmt)->head; - if (head == goto_ifnot_sym) { + if (head == symbolicgoto_sym) { + assert(jl_array_len(((jl_expr_t*)stmt)->args) == 1); + i = symboliclabel_idx(jl_cellref(((jl_expr_t*)stmt)->args, 0), + stmts); + continue; + } + else if (head == goto_ifnot_sym) { jl_value_t *cond = eval(jl_exprarg(stmt,0), locals, nl); if (cond == jl_false) { i = label_idx(jl_exprarg(stmt,1), stmts); diff --git a/src/jltypes.c b/src/jltypes.c index 63d5c227aebad..1916547197823 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3222,6 +3222,8 @@ void jl_init_types(void) goto_sym = jl_symbol("goto"); goto_ifnot_sym = jl_symbol("gotoifnot"); label_sym = jl_symbol("label"); + symboliclabel_sym = jl_symbol("symboliclabel"); + symbolicgoto_sym = jl_symbol("symbolicgoto"); return_sym = jl_symbol("return"); lambda_sym = jl_symbol("lambda"); macro_sym = jl_symbol("macro"); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 47efcb66cfa7e..2edf62d445f0b 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2987,6 +2987,19 @@ So far only the second case can actually occur. (let ((l (make&mark-label))) (set! label-map (cons (cons (cadr e) l) label-map)))))) + ((symboliclabel) (let ((m (assq (cadr e) label-map))) + (if m + (emit `(label ,(cdr m))) + (let ((l (make&mark-label))) + (set! label-map + (cons (cons (cadr e) l) label-map)))))) + ((symbolicgoto) (let ((m (assq (cadr e) label-map))) + (if m + (emit `(goto ,(cdr m))) + (let ((l (make-label))) + (set! label-map + (cons (cons (cadr e) l) label-map)) + (emit `(goto ,l)))))) ((type_goto) (let ((m (assq (cadr e) label-map))) (if m (emit `(type_goto ,(cdr m) ,@(cddr e))) @@ -3155,11 +3168,11 @@ So far only the second case can actually occur. (define (resolve-expansion-vars- e env m inarg) (cond ((or (eq? e 'true) (eq? e 'false) (eq? e 'end)) e) - ((symbol? e) - (let ((a (assq e env))) - (if a (cdr a) - (if m `(|.| ,m (quote ,e)) - e)))) + ((symbol? e) + (let ((a (assq e env))) + (if a (cdr a) + (if m `(|.| ,m (quote ,e)) + e)))) ((or (not (pair? e)) (quoted? e)) e) (else @@ -3170,6 +3183,8 @@ So far only the second case can actually occur. `(macrocall ,.(map (lambda (x) (resolve-expansion-vars- x env m inarg)) (cdr e)))) + ((symboliclabel) e) + ((symbolicgoto) e) ((type) `(type ,(cadr e) ,(resolve-expansion-vars- (caddr e) env m inarg) ;; type has special behavior: identifiers inside are diff --git a/src/julia.h b/src/julia.h index 3698e2b410638..5047a48a76fb2 100644 --- a/src/julia.h +++ b/src/julia.h @@ -414,6 +414,7 @@ extern jl_sym_t *export_sym; extern jl_sym_t *import_sym; extern jl_sym_t *importall_sym; extern jl_sym_t *using_sym; extern jl_sym_t *goto_sym; extern jl_sym_t *goto_ifnot_sym; extern jl_sym_t *label_sym; extern jl_sym_t *return_sym; +extern jl_sym_t *symboliclabel_sym; extern jl_sym_t *symbolicgoto_sym; extern jl_sym_t *lambda_sym; extern jl_sym_t *assign_sym; extern jl_sym_t *null_sym; extern jl_sym_t *body_sym; extern jl_sym_t *macro_sym; extern jl_sym_t *method_sym; From 075af30224a319348bef525b71fca6fcddf0d5ac Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Sat, 8 Feb 2014 16:38:27 -0800 Subject: [PATCH 02/11] Try using a table in goto-form. --- src/julia-syntax.scm | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 2edf62d445f0b..56f2c761c3034 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2896,7 +2896,7 @@ So far only the second case can actually occur. (define (goto-form e) (let ((code '()) (label-counter 0) - (label-map '()) + (label-map (table)) (handler-level 0)) (define (emit c) (set! code (cons c code))) @@ -2981,31 +2981,27 @@ So far only the second case can actually occur. (if (> handler-level 0) (emit `(leave ,handler-level))) (emit (goto-form e)))) - ((label) (let ((m (assq (cadr e) label-map))) + ((label) (let ((m (get label-map (cadr e) #f))) (if m - (emit `(label ,(cdr m))) + (emit `(label ,m)) (let ((l (make&mark-label))) - (set! label-map - (cons (cons (cadr e) l) label-map)))))) - ((symboliclabel) (let ((m (assq (cadr e) label-map))) + (put! label-map (cadr e) l))))) + ((symboliclabel) (let ((m (get label-map (cadr e) #f))) (if m - (emit `(label ,(cdr m))) + (emit `(label ,m)) (let ((l (make&mark-label))) - (set! label-map - (cons (cons (cadr e) l) label-map)))))) - ((symbolicgoto) (let ((m (assq (cadr e) label-map))) + (put! label-map (cadr e) l))))) + ((symbolicgoto) (let ((m (get label-map (cadr e) #f))) (if m - (emit `(goto ,(cdr m))) + (emit `(goto ,m)) (let ((l (make-label))) - (set! label-map - (cons (cons (cadr e) l) label-map)) + (put! label-map (cadr e) l) (emit `(goto ,l)))))) - ((type_goto) (let ((m (assq (cadr e) label-map))) + ((type_goto) (let ((m (get label-map (cadr e) #f))) (if m - (emit `(type_goto ,(cdr m) ,@(cddr e))) + (emit `(type_goto ,m ,@(cddr e))) (let ((l (make-label))) - (set! label-map - (cons (cons (cadr e) l) label-map)) + (put! label-map (cadr e) l) (emit `(type_goto ,l ,@(cddr e))))))) ;; exception handlers are lowered using ;; (enter L) - push handler with catch block at label L From 33e0c745855798f218a3a27ddde2753d8d1759b0 Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Tue, 11 Feb 2014 22:51:13 -0800 Subject: [PATCH 03/11] Lookup labels using a Dict in typeinf. --- base/inference.jl | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index e7726062e1c47..ee947b57edd57 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -1168,13 +1168,11 @@ function stchanged(new::Union(StateUpdate,VarTable), old, vars) end function findlabel(body, l) - for i=1:length(body) - b = body[i] - if isa(b,LabelNode) && b.label==l - return i - end + i = get(labels, l, nothing) + if i === nothing + error("label ",l," not found") end - error("label ",l," not found") + return i end function label_counter(body) @@ -1292,6 +1290,14 @@ function typeinf(linfo::LambdaStaticData,atypes::Tuple,sparams::Tuple, def, cop) body = (ast.args[3].args)::Array{Any,1} n = length(body) + labels = Dict{Int, Int}() + for i=1:length(body) + b = body[i] + if isa(b,LabelNode) + labels[b.label] = i + end + end + # our stack frame frame = CallStack(ast0, linfo.module, atypes, inference_stack) inference_stack = frame @@ -1391,12 +1397,12 @@ function typeinf(linfo::LambdaStaticData,atypes::Tuple,sparams::Tuple, def, cop) end pc´ = pc+1 if isa(stmt,GotoNode) - pc´ = findlabel(body,stmt.label) + pc´ = findlabel(labels,stmt.label) elseif isa(stmt,Expr) hd = stmt.head if is(hd,:gotoifnot) condexpr = stmt.args[1] - l = findlabel(body,stmt.args[2]) + l = findlabel(labels,stmt.args[2]) # constant conditions if is(condexpr,true) elseif is(condexpr,false) @@ -1410,7 +1416,7 @@ function typeinf(linfo::LambdaStaticData,atypes::Tuple,sparams::Tuple, def, cop) end end elseif is(hd,:type_goto) - l = findlabel(body,stmt.args[1]) + l = findlabel(labels,stmt.args[1]) for i = 2:length(stmt.args) var = stmt.args[i] if isa(var,SymbolNode) @@ -1465,7 +1471,7 @@ function typeinf(linfo::LambdaStaticData,atypes::Tuple,sparams::Tuple, def, cop) end end elseif is(hd,:enter) - l = findlabel(body,stmt.args[1]::Int) + l = findlabel(labels,stmt.args[1]::Int) cur_hand = (l,cur_hand) handler_at[l] = cur_hand elseif is(hd,:leave) From 144553dab7005bc7957cb0316eeafc441b7af7a1 Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Fri, 28 Mar 2014 22:07:38 -0700 Subject: [PATCH 04/11] Use an array instead of a dict to index labels. --- base/inference.jl | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index ee947b57edd57..4652c37e89e88 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -1167,9 +1167,9 @@ function stchanged(new::Union(StateUpdate,VarTable), old, vars) return false end -function findlabel(body, l) - i = get(labels, l, nothing) - if i === nothing +function findlabel(labels, l) + i = l+1 > length(labels) ? 0 : labels[l+1] + if i == 0 error("label ",l," not found") end return i @@ -1290,11 +1290,19 @@ function typeinf(linfo::LambdaStaticData,atypes::Tuple,sparams::Tuple, def, cop) body = (ast.args[3].args)::Array{Any,1} n = length(body) - labels = Dict{Int, Int}() + maxlabel = 0 for i=1:length(body) b = body[i] if isa(b,LabelNode) - labels[b.label] = i + maxlabel = max(maxlabel, b.label+1) + end + end + labels = zeros(Int, maxlabel) + + for i=1:length(body) + b = body[i] + if isa(b,LabelNode) + labels[b.label+1] = i end end From 9670fa2882370afbe7dd1a245603a066304421d2 Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Sat, 29 Mar 2014 20:28:01 -0700 Subject: [PATCH 05/11] Macro hygiene and error messages for gotos. --- src/julia-syntax.scm | 64 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 56f2c761c3034..d5002757fa14d 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2728,6 +2728,50 @@ So far only the second case can actually occur. (flatten-scopes x))) e)))) +(define (find-symbolic-label-defs e tbl) + (if (or (not (pair? e)) (quoted? e)) + '() + (if (eq? (car e) 'symboliclabel) + (put! tbl (cadr e) #t) + (map (lambda (x) (find-symbolic-label-defs x tbl)) e)))) + +(define (find-symbolic-label-refs e tbl) + (if (or (not (pair? e)) (quoted? e)) + '() + (if (eq? (car e) 'symbolicgoto) + (put! tbl (cadr e) #t) + (map (lambda (x) (find-symbolic-label-refs x tbl)) e)))) + +(define (find-symbolic-labels e) + (let + ((defs (table)) + (refs (table))) + (find-symbolic-label-defs e defs) + (find-symbolic-label-refs e refs) + (table.foldl + (lambda (label v labels) + (if (has? refs label) + (cons label labels) + labels)) + '() defs))) + +(define (rename-symbolic-labels- e relabel) + (cond + ((or (not (pair? e)) (quoted? e)) e) + ((eq? (car e) 'symbolicgoto) + (let ((newlabel (assq (cadr e) relabel))) + (if newlabel `(symbolicgoto ,(cdr newlabel)) e))) + ((eq? (car e) 'symboliclabel) + (let ((newlabel (assq (cadr e) relabel))) + (if newlabel `(symboliclabel ,(cdr newlabel)) e))) + (else (map (lambda (x) (rename-symbolic-labels- x relabel)) e)))) + +(define (rename-symbolic-labels e) + (let* + ((labels (find-symbolic-labels e)) + (relabel (pair-with-gensyms labels))) + (rename-symbolic-labels- e relabel))) + (define (make-var-info name) (list name 'Any 0)) (define vinfo:name car) (define vinfo:type cadr) @@ -2897,6 +2941,7 @@ So far only the second case can actually occur. (let ((code '()) (label-counter 0) (label-map (table)) + (label-decl (table)) (handler-level 0)) (define (emit c) (set! code (cons c code))) @@ -2985,12 +3030,16 @@ So far only the second case can actually occur. (if m (emit `(label ,m)) (let ((l (make&mark-label))) - (put! label-map (cadr e) l))))) + (put! label-map (cadr e) l))) + (put! label-decl (cadr e) #t))) ((symboliclabel) (let ((m (get label-map (cadr e) #f))) (if m - (emit `(label ,m)) + (if (get label-decl (cadr e) #f) + (error (string "label \"" (cadr e) "\" defined multiple times")) + (emit `(label ,m))) (let ((l (make&mark-label))) - (put! label-map (cadr e) l))))) + (put! label-map (cadr e) l))) + (put! label-decl (cadr e) #t))) ((symbolicgoto) (let ((m (get label-map (cadr e) #f))) (if m (emit `(goto ,m)) @@ -3051,6 +3100,10 @@ So far only the second case can actually occur. ((eq? (car e) 'lambda) (compile (cadddr e) '() (append (cadr (caddr e)) (caddr (caddr e)))) + (table.foreach (lambda (l m) + (if (not (get label-decl l #f)) + (error (string "label \"" l "\" referenced but not defined")))) + label-map) `(lambda ,(cadr e) ,(caddr e) ,(cons 'body (reverse! code)))) (else (cons (car e) @@ -3120,8 +3173,9 @@ So far only the second case can actually occur. (let ((form (car form)) (m (cdr form))) ;; m is the macro's def module, or #f if def env === use env - (julia-expand-macros- - (resolve-expansion-vars form m))))) + (rename-symbolic-labels + (julia-expand-macros- + (resolve-expansion-vars form m)))))) (else (map julia-expand-macros- e)))) From 59b3970adc0a34c66faa1b1b9bf170ab8dd74b71 Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Tue, 3 Jun 2014 13:48:01 -0700 Subject: [PATCH 06/11] Remove unecessary interpreter changes. --- src/interpreter.c | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/interpreter.c b/src/interpreter.c index a4d80218e816d..4587803921c9e 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -1,5 +1,4 @@ #include -#include #include #include #ifdef _OS_WINDOWS_ @@ -471,25 +470,6 @@ static int label_idx(jl_value_t *tgt, jl_array_t *stmts) return j; } -static int symboliclabel_idx(jl_value_t *tgt, jl_array_t *stmts) -{ - size_t j; - jl_sym_t* stgt = (jl_sym_t*)tgt; - assert(jl_is_symbol(tgt)); - for(j=0; j < stmts->nrows; j++) { - jl_value_t *l = jl_cellref(stmts,j); - if (jl_is_expr(l) && ((jl_expr_t*)l)->head == symboliclabel_sym) { - assert(jl_array_len(((jl_expr_t*)l)->args)); - jl_value_t* labeltgt = jl_cellref(((jl_expr_t*)l)->args, 0); - if (jl_is_symbol(labeltgt) && (jl_sym_t*)labeltgt == stgt) { - break; - } - } - } - assert(j < stmts->nrows); - return j; -} - jl_value_t *jl_toplevel_eval_body(jl_array_t *stmts) { return eval_body(stmts, NULL, 0, 0, 1); @@ -509,13 +489,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, } if (jl_is_expr(stmt)) { jl_sym_t *head = ((jl_expr_t*)stmt)->head; - if (head == symbolicgoto_sym) { - assert(jl_array_len(((jl_expr_t*)stmt)->args) == 1); - i = symboliclabel_idx(jl_cellref(((jl_expr_t*)stmt)->args, 0), - stmts); - continue; - } - else if (head == goto_ifnot_sym) { + if (head == goto_ifnot_sym) { jl_value_t *cond = eval(jl_exprarg(stmt,0), locals, nl); if (cond == jl_false) { i = label_idx(jl_exprarg(stmt,1), stmts); From ee9d739624e3ed9de91e70055326e6b3224c2727 Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Sun, 8 Jun 2014 23:59:44 -0700 Subject: [PATCH 07/11] Make goto play nice with try/catch blocks. --- src/julia-syntax.scm | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index d5002757fa14d..6501871c93e2c 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2728,6 +2728,17 @@ So far only the second case can actually occur. (flatten-scopes x))) e)))) +(define (symbolic-label-handler-levels e levels handler-level) + (if (or (not (pair? e)) (quoted? e)) + '() + (case (car e) + ((trycatch) + (symbolic-label-handler-levels (cadr e) levels (+ handler-level 1))) + ((symboliclabel) + (put! levels (cadr e) handler-level)) + (else + (map (lambda (x) (symbolic-label-handler-levels x levels handler-level)) e))))) + (define (find-symbolic-label-defs e tbl) (if (or (not (pair? e)) (quoted? e)) '() @@ -2942,7 +2953,9 @@ So far only the second case can actually occur. (label-counter 0) (label-map (table)) (label-decl (table)) + (label-level (table)) (handler-level 0)) + (symbolic-label-handler-levels e label-level 0) (define (emit c) (set! code (cons c code))) (define (make-label) @@ -3040,13 +3053,22 @@ So far only the second case can actually occur. (let ((l (make&mark-label))) (put! label-map (cadr e) l))) (put! label-decl (cadr e) #t))) - ((symbolicgoto) (let ((m (get label-map (cadr e) #f))) + ((symbolicgoto) (let + ((m (get label-map (cadr e) #f)) + (target-level (get label-level (cadr e) #f))) + (cond + ((not target-level) + (error (string "label \"" (cadr e) "\" referenced but not defined"))) + ((> target-level handler-level) + (error (string "cannot goto label \"" (cadr e) "\" inside try/catch block"))) + ((< target-level handler-level) + (emit `(leave ,(- handler-level target-level))))) (if m (emit `(goto ,m)) (let ((l (make-label))) - (put! label-map (cadr e) l) - (emit `(goto ,l)))))) - ((type_goto) (let ((m (get label-map (cadr e) #f))) + (put! label-map (cadr e) l) + (emit `(goto ,l)))))) + ((type_goto) (let((m (get label-map (cadr e) #f))) (if m (emit `(type_goto ,m ,@(cddr e))) (let ((l (make-label))) @@ -3100,10 +3122,6 @@ So far only the second case can actually occur. ((eq? (car e) 'lambda) (compile (cadddr e) '() (append (cadr (caddr e)) (caddr (caddr e)))) - (table.foreach (lambda (l m) - (if (not (get label-decl l #f)) - (error (string "label \"" l "\" referenced but not defined")))) - label-map) `(lambda ,(cadr e) ,(caddr e) ,(cons 'body (reverse! code)))) (else (cons (car e) From 1efcf81b1b11ac9d8eb796e96752e446ded3f22b Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Mon, 9 Jun 2014 00:16:46 -0700 Subject: [PATCH 08/11] Basic goto tests. --- test/Makefile | 2 +- test/goto.jl | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 test/goto.jl diff --git a/test/Makefile b/test/Makefile index e2aec4fd1cf12..e1c790e37ed19 100644 --- a/test/Makefile +++ b/test/Makefile @@ -8,7 +8,7 @@ TESTS = all core keywordargs numbers strings unicode collections hashing \ git pkg resolve suitesparse complex version pollfd mpfr broadcast \ socket floatapprox priorityqueue readdlm regex float16 combinatorics \ sysinfo rounding ranges mod2pi euler show lineedit \ - replcompletions backtrace repl test + replcompletions backtrace repl test goto default: all diff --git a/test/goto.jl b/test/goto.jl new file mode 100644 index 0000000000000..32d69c1d5ee4f --- /dev/null +++ b/test/goto.jl @@ -0,0 +1,58 @@ + +# Basic goto tests + +function goto_test1() + @goto a + return false + @label a + return true +end +@test goto_test1() + + +@test_throws ErrorException eval( + quote + function goto_test2() + @goto a + @label a + @label a + return + end + end) + + +@test_throws ErrorException eval( + quote + function goto_test3() + @goto a + return + end + end) + + +@test_throws ErrorException eval( + quote + function goto_test4() + @goto a + try + @label a + catch + end + end + end) + + +# test that labels in macros are reassigned +macro goto_test5_macro() + @label a +end + +@test_throws ErrorException eval( + quote + function goto_test5() + @goto a + @goto_test5_macro + return + end + end) + From e56fb9d5e13d8009ded67a6d500475a4466c1261 Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Mon, 9 Jun 2014 09:56:20 -0700 Subject: [PATCH 09/11] Forbid gotos from finally blocks. --- src/julia-syntax.scm | 10 ++++++++++ test/goto.jl | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 6501871c93e2c..2c3fa27728ff3 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1104,6 +1104,8 @@ (var (caddr e)) (catchb (cadddr e)) (finalb (cadddr (cdr e)))) + (if (has-unmatched-symbolic-goto? finalb) + (error "goto from a finally block is not permitted")) (let ((hasret (or (contains return? tryb) (contains return? catchb)))) (let ((err (gensy)) @@ -2728,6 +2730,14 @@ So far only the second case can actually occur. (flatten-scopes x))) e)))) +(define (has-unmatched-symbolic-goto? e) + (let ((label-refs (table)) + (label-defs (table))) + (find-symbolic-label-refs e label-refs) + (find-symbolic-label-defs e label-defs) + (any not (map (lambda (k) (get label-defs k #f)) + (table.keys label-refs))))) + (define (symbolic-label-handler-levels e levels handler-level) (if (or (not (pair? e)) (quoted? e)) '() diff --git a/test/goto.jl b/test/goto.jl index 32d69c1d5ee4f..b7519755a9dc6 100644 --- a/test/goto.jl +++ b/test/goto.jl @@ -56,3 +56,16 @@ end end end) + +@test_throws ErrorException eval( + quote + function goto_test6() + try + finally + @goto a + end + @label a + return + end + end) + From e5d898b9f5ab4e7147cf53f27364d388ca782fc1 Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Mon, 9 Jun 2014 10:05:02 -0700 Subject: [PATCH 10/11] Forbid goto skipping finally blocks. --- src/julia-syntax.scm | 4 ++-- test/goto.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 2c3fa27728ff3..9a71d36796c4d 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1104,8 +1104,8 @@ (var (caddr e)) (catchb (cadddr e)) (finalb (cadddr (cdr e)))) - (if (has-unmatched-symbolic-goto? finalb) - (error "goto from a finally block is not permitted")) + (if (has-unmatched-symbolic-goto? tryb) + (error "goto from a try/finally block is not permitted")) (let ((hasret (or (contains return? tryb) (contains return? catchb)))) (let ((err (gensy)) diff --git a/test/goto.jl b/test/goto.jl index b7519755a9dc6..145056ec091d7 100644 --- a/test/goto.jl +++ b/test/goto.jl @@ -61,8 +61,8 @@ end quote function goto_test6() try - finally @goto a + finally end @label a return From 600f10dd5b5aa03bc1f9ca7925413e8809f95d6b Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Mon, 16 Jun 2014 16:02:06 -0700 Subject: [PATCH 11/11] Remove unused symbolicgoto_sym and symboliclabel_sym globals --- src/alloc.c | 1 - src/jltypes.c | 2 -- src/julia.h | 1 - 3 files changed, 4 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index b6ed6f90ce83f..1c59431460a5f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -77,7 +77,6 @@ jl_sym_t *line_sym; jl_sym_t *jl_incomplete_sym; // head symbols for each expression type jl_sym_t *goto_sym; jl_sym_t *goto_ifnot_sym; jl_sym_t *label_sym; jl_sym_t *return_sym; -jl_sym_t *symboliclabel_sym; jl_sym_t *symbolicgoto_sym; jl_sym_t *lambda_sym; jl_sym_t *assign_sym; jl_sym_t *null_sym; jl_sym_t *body_sym; jl_sym_t *macro_sym; jl_sym_t *method_sym; diff --git a/src/jltypes.c b/src/jltypes.c index 1916547197823..63d5c227aebad 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3222,8 +3222,6 @@ void jl_init_types(void) goto_sym = jl_symbol("goto"); goto_ifnot_sym = jl_symbol("gotoifnot"); label_sym = jl_symbol("label"); - symboliclabel_sym = jl_symbol("symboliclabel"); - symbolicgoto_sym = jl_symbol("symbolicgoto"); return_sym = jl_symbol("return"); lambda_sym = jl_symbol("lambda"); macro_sym = jl_symbol("macro"); diff --git a/src/julia.h b/src/julia.h index 5047a48a76fb2..3698e2b410638 100644 --- a/src/julia.h +++ b/src/julia.h @@ -414,7 +414,6 @@ extern jl_sym_t *export_sym; extern jl_sym_t *import_sym; extern jl_sym_t *importall_sym; extern jl_sym_t *using_sym; extern jl_sym_t *goto_sym; extern jl_sym_t *goto_ifnot_sym; extern jl_sym_t *label_sym; extern jl_sym_t *return_sym; -extern jl_sym_t *symboliclabel_sym; extern jl_sym_t *symbolicgoto_sym; extern jl_sym_t *lambda_sym; extern jl_sym_t *assign_sym; extern jl_sym_t *null_sym; extern jl_sym_t *body_sym; extern jl_sym_t *macro_sym; extern jl_sym_t *method_sym;