Each name an as binds is a slot of its own under its name, so the break loop's locals show it with what the test found.
This commit is contained in:
parent
09ffc27883
commit
83bb65957d
66
lib/check.ml
66
lib/check.ml
@ -3184,12 +3184,8 @@ let unnarrowable_in (body : Ast.expr list) =
|
|||||||
List.iter (walk ~in_fn:false) body;
|
List.iter (walk ~in_fn:false) body;
|
||||||
!out
|
!out
|
||||||
|
|
||||||
(* A name an [as] bound to what an Option held: the slot holding the Option,
|
|
||||||
read as its payload, as a narrowed local is, but not assignable. *)
|
|
||||||
let as_tag = "~as"
|
|
||||||
|
|
||||||
let local_of loc (b : binding) =
|
let local_of loc (b : binding) =
|
||||||
if b.bwhat = Some narrowed_tag || b.bwhat = Some as_tag then
|
if b.bwhat = Some narrowed_tag then
|
||||||
mk loc b.bty (Tast.Field (mk loc (Types.Option b.bty) (Tast.Local b.slot), 1))
|
mk loc b.bty (Tast.Field (mk loc (Types.Option b.bty) (Tast.Local b.slot), 1))
|
||||||
else mk loc b.bty (Tast.Local b.slot)
|
else mk loc b.bty (Tast.Local b.slot)
|
||||||
|
|
||||||
@ -10797,8 +10793,8 @@ and as_name g = Ast.as_name g
|
|||||||
(* A condition with [as] in it, as the bool it tests, and each name it binds
|
(* A condition with [as] in it, as the bool it tests, and each name it binds
|
||||||
with the hidden name the block reads it through. The chain runs left to
|
with the hidden name the block reads it through. The chain runs left to
|
||||||
right and stops at the first test that fails, so each value is found
|
right and stops at the first test that fails, so each value is found
|
||||||
once. What an [as] tests is held in one slot, and its name reads that
|
once. Each name an [as] binds is one slot, read by the rest of the chain
|
||||||
slot: the payload of an Option held there, or the dyn itself. *)
|
and, through [Ast.Alias], by the block. *)
|
||||||
and as_cond ctx (c : Ast.expr) =
|
and as_cond ctx (c : Ast.expr) =
|
||||||
let named = ref [] in
|
let named = ref [] in
|
||||||
let no loc = mk loc Types.Bool (Tast.Bool false) in
|
let no loc = mk loc Types.Bool (Tast.Bool false) in
|
||||||
@ -10820,26 +10816,44 @@ and as_cond ctx (c : Ast.expr) =
|
|||||||
| Ast.IfLet (e, { Ast.pat = Ast.Pctor (g, []); body = [ q ]; _ },
|
| Ast.IfLet (e, { Ast.pat = Ast.Pctor (g, []); body = [ q ]; _ },
|
||||||
Some { Ast.e = Ast.Var "false"; _ }) when as_name g ->
|
Some { Ast.e = Ast.Var "false"; _ }) when as_name g ->
|
||||||
let ev = check ctx e in
|
let ev = check ctx e in
|
||||||
let hs = fresh_slot ctx ev.Tast.ty in
|
let refuse t =
|
||||||
let hv = mk loc ev.Tast.ty (Tast.Local hs) in
|
Loc.failk "check/as-not-optional" e.Ast.loc
|
||||||
let test, what, ty =
|
"%s is %s, which always holds a value, so as has nothing to test. \
|
||||||
match ev.Tast.ty with
|
as names what an Option or a dyn holds, when it holds something. \
|
||||||
| Types.Option t -> (opt_is_some loc hv, Some as_tag, t)
|
It is not a conversion: a number is converted with its type's \
|
||||||
| Types.Dyn -> (dyn_not_nil loc hv, None, Types.Dyn)
|
name, as in i32(x)"
|
||||||
| t ->
|
(source_text e) (tyname loc t)
|
||||||
Loc.failk "check/as-not-optional" e.Ast.loc
|
|
||||||
"%s is %s, which always holds a value, so as has nothing to test. \
|
|
||||||
as names what an Option or a dyn holds, when it holds something. \
|
|
||||||
It is not a conversion: a number is converted with its type's \
|
|
||||||
name, as in i32(x)"
|
|
||||||
(source_text e) (tyname loc t)
|
|
||||||
in
|
in
|
||||||
let b = { slot = hs; bty = ty; assignable = false; bwhat = what; blit = None } in
|
(* [g] is a slot of its own under its own name, so locals, the stepper,
|
||||||
incr held_n;
|
the inspector and the watch view show it as the program reads it. A
|
||||||
named := (g, Printf.sprintf "~as%d" !held_n, b) :: !named;
|
dyn is held there directly; an Option is held in a hidden slot and
|
||||||
let qv = scoped ctx (fun () -> ctx.scope <- (g, b) :: ctx.scope; go q) in
|
its payload copied into [g]'s once the test holds. *)
|
||||||
mk loc Types.Bool
|
let bound ty =
|
||||||
(Tast.Let ([ (hs, ev) ], [ mk loc Types.Bool (Tast.If (test, qv, no loc)) ]))
|
scoped ctx (fun () ->
|
||||||
|
let slot = bind ctx g ty ~assignable:false in
|
||||||
|
let b = Option.get (lookup ctx g) in
|
||||||
|
incr held_n;
|
||||||
|
named := (g, Printf.sprintf "~as%d" !held_n, b) :: !named;
|
||||||
|
(slot, go q))
|
||||||
|
in
|
||||||
|
(match ev.Tast.ty with
|
||||||
|
| Types.Option t ->
|
||||||
|
let hs = fresh_slot ctx ev.Tast.ty in
|
||||||
|
let hv = mk loc ev.Tast.ty (Tast.Local hs) in
|
||||||
|
let slot, qv = bound t in
|
||||||
|
mk loc Types.Bool
|
||||||
|
(Tast.Let ([ (hs, ev) ],
|
||||||
|
[ mk loc Types.Bool
|
||||||
|
(Tast.If (opt_is_some loc hv,
|
||||||
|
mk loc Types.Bool
|
||||||
|
(Tast.Let ([ (slot, opt_payload loc t hv) ], [ qv ])),
|
||||||
|
no loc)) ]))
|
||||||
|
| Types.Dyn ->
|
||||||
|
let slot, qv = bound Types.Dyn in
|
||||||
|
let sv = mk loc Types.Dyn (Tast.Local slot) in
|
||||||
|
mk loc Types.Bool
|
||||||
|
(Tast.Let ([ (slot, ev) ], [ mk loc Types.Bool (Tast.If (dyn_not_nil loc sv, qv, no loc)) ]))
|
||||||
|
| t -> refuse t)
|
||||||
| _ -> check_truthy ctx c
|
| _ -> check_truthy ctx c
|
||||||
in
|
in
|
||||||
let cv = go c in
|
let cv = go c in
|
||||||
|
|||||||
26
test/programs/dev-as.fln
Normal file
26
test/programs/dev-as.fln
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
;; A program that stops inside an if o as g block (decision 136): the break
|
||||||
|
;; loop's locals list g, with the value the test found, on both backends.
|
||||||
|
import agent "vendor:agent"
|
||||||
|
|
||||||
|
struct Boom
|
||||||
|
why: i32
|
||||||
|
|
||||||
|
let ticks: i64 = 0
|
||||||
|
|
||||||
|
fn look(o: i32?, d) -> i64
|
||||||
|
if o as g and g > 1 and d as e
|
||||||
|
restart-case
|
||||||
|
error(Boom{.why g})
|
||||||
|
0
|
||||||
|
restart carry-on()
|
||||||
|
5
|
||||||
|
else
|
||||||
|
0
|
||||||
|
|
||||||
|
fn main() -> i32
|
||||||
|
agent/start("/tmp/flan-dev-as-fallback.sock")
|
||||||
|
println(look(Some(41), "hi"))
|
||||||
|
for i in range(4000)
|
||||||
|
agent/wait(5)
|
||||||
|
ticks += 1
|
||||||
|
0
|
||||||
@ -2483,6 +2483,75 @@ let () =
|
|||||||
null_park "--llvm";
|
null_park "--llvm";
|
||||||
null_park "--x86";
|
null_park "--x86";
|
||||||
|
|
||||||
|
(* A stop inside an [if o as g and ... and d as e] block (decision 136):
|
||||||
|
each name an [as] binds is a slot of its own under its name, so the
|
||||||
|
locals list it with what the test found. *)
|
||||||
|
let as_locals backend =
|
||||||
|
let asock = tmp ("as" ^ backend ^ ".sock") and aout = tmp ("as" ^ backend ^ ".out") in
|
||||||
|
(try Sys.remove asock with Sys_error _ -> ());
|
||||||
|
let afd = Unix.openfile aout [ Unix.O_WRONLY; Unix.O_CREAT; Unix.O_TRUNC ] 0o600 in
|
||||||
|
let apid =
|
||||||
|
Unix.create_process flan
|
||||||
|
[| flan; "dev"; "programs/dev-as.fln"; "-s"; asock; backend |]
|
||||||
|
Unix.stdin afd Unix.stderr
|
||||||
|
in
|
||||||
|
Unix.close afd;
|
||||||
|
if not (listening ~pid:apid asock) then begin
|
||||||
|
fail "the as daemon (%s) %s" backend !listen_why;
|
||||||
|
(try Unix.kill apid Sys.sigkill with Unix.Unix_error _ -> ())
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
let c = connect asock in
|
||||||
|
let ask sexp = Wire.parse (Wire.send c sexp; Wire.recv c) in
|
||||||
|
let stopped r =
|
||||||
|
match Wire.field r "stopped" with
|
||||||
|
| Some { Form.v = Form.Sym "t"; _ } -> true
|
||||||
|
| _ -> false
|
||||||
|
in
|
||||||
|
if not (await (fun () -> stopped (ask "(:op \"describe\")"))) then
|
||||||
|
fail "the as program never stopped (%s)" backend
|
||||||
|
else begin
|
||||||
|
let r = ask "(:op \"locals\" :frame 0)" in
|
||||||
|
let rows =
|
||||||
|
match Wire.field r "locals" with
|
||||||
|
| Some { Form.v = Form.List l; _ } ->
|
||||||
|
List.filter_map
|
||||||
|
(fun (e : Form.t) ->
|
||||||
|
match e.Form.v with
|
||||||
|
| Form.List ({ Form.v = Form.Str n; _ } :: { Form.v = Form.Str ty; _ }
|
||||||
|
:: { Form.v = Form.Str v; _ } :: _) -> Some (n, (ty, v))
|
||||||
|
| _ -> None)
|
||||||
|
l
|
||||||
|
| _ -> []
|
||||||
|
in
|
||||||
|
(match List.assoc_opt "g" rows with
|
||||||
|
| Some ("i32", "41") -> ()
|
||||||
|
| Some (ty, v) -> fail "locals show g as %s %s (%s)" ty v backend
|
||||||
|
| None ->
|
||||||
|
fail "locals do not show g (%s): %s" backend
|
||||||
|
(String.concat " " (List.map fst rows)));
|
||||||
|
(match List.assoc_opt "e" rows with
|
||||||
|
| Some ("dyn", v) when Test_support.contains v "hi" -> ()
|
||||||
|
| Some (ty, v) -> fail "locals show e as %s %s (%s)" ty v backend
|
||||||
|
| None -> fail "locals do not show e (%s)" backend)
|
||||||
|
end;
|
||||||
|
ignore (ask "(:op \"close\")");
|
||||||
|
Unix.close c;
|
||||||
|
if not
|
||||||
|
(await ~ms:5000 (fun () ->
|
||||||
|
match Unix.waitpid [ Unix.WNOHANG ] apid with
|
||||||
|
| 0, _ -> false
|
||||||
|
| _ -> true
|
||||||
|
| exception Unix.Unix_error _ -> true))
|
||||||
|
then begin
|
||||||
|
(try Unix.kill apid Sys.sigkill with Unix.Unix_error _ -> ());
|
||||||
|
(try ignore (Unix.waitpid [] apid) with Unix.Unix_error _ -> ())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
in
|
||||||
|
as_locals "--llvm";
|
||||||
|
as_locals "--x86";
|
||||||
|
|
||||||
(* ── The locals of a stopped frame ─────────────────────────────── *)
|
(* ── The locals of a stopped frame ─────────────────────────────── *)
|
||||||
|
|
||||||
(* A third daemon, over a program that stops with something worth looking
|
(* A third daemon, over a program that stops with something worth looking
|
||||||
|
|||||||
@ -2072,9 +2072,9 @@ let () =
|
|||||||
(match Session.eval ~pause:(1, col) t paren with
|
(match Session.eval ~pause:(1, col) t paren with
|
||||||
| _ -> ()
|
| _ -> ()
|
||||||
| exception Loc.Error d -> fail "a mark at (and: %s" d.Loc.dmsg);
|
| exception Loc.Error d -> fail "a mark at (and: %s" d.Loc.dmsg);
|
||||||
(* What an as finds is held once, and its name reads that slot: one
|
(* What an as finds is copied once, into g's own named slot, which the
|
||||||
binding in the function, where a copy into g's own slot and another
|
rest of the chain and the block read: two bindings, the Option held
|
||||||
into the block's would be three. *)
|
and g, where another copy into the block's would be three. *)
|
||||||
ignore
|
ignore
|
||||||
(Source.with_code ~syntax:Source.Indented ~at:None (fun () ->
|
(Source.with_code ~syntax:Source.Indented ~at:None (fun () ->
|
||||||
Session.eval ~origin:"copies.fln" t
|
Session.eval ~origin:"copies.fln" t
|
||||||
@ -2089,6 +2089,8 @@ let () =
|
|||||||
(Tast.walk (fun (e : Tast.expr) ->
|
(Tast.walk (fun (e : Tast.expr) ->
|
||||||
match e.Tast.e with Tast.Let (bs, _) -> n := !n + List.length bs | _ -> ()))
|
match e.Tast.e with Tast.Let (bs, _) -> n := !n + List.length bs | _ -> ()))
|
||||||
f.Tast.body;
|
f.Tast.body;
|
||||||
if !n <> 1 then fail "if o? as g binds %d slots, wanted 1" !n);
|
if !n <> 2 then fail "if o? as g binds %d slots, wanted 2" !n;
|
||||||
|
if not (Array.exists (fun x -> x = Some "g") f.Tast.snames) then
|
||||||
|
fail "if o? as g has no slot named g");
|
||||||
|
|
||||||
Test_support.report ~label:"session" ()
|
Test_support.report ~label:"session" ()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user