add anon functions; remove pre-block declarations; update tests

This commit is contained in:
galfour 2019-11-18 16:10:48 +01:00
parent 5422049dba
commit ddc4b7b7a5
25 changed files with 286 additions and 208 deletions

View File

@ -210,9 +210,9 @@ and type_tuple = (type_expr, comma) nsepseq par reg
(* Function and procedure declarations *)
and fun_decl = {
and fun_expr = {
kwd_function : kwd_function;
name : variable;
name : variable option;
param : parameters;
colon : colon;
ret_type : type_expr;
@ -221,7 +221,11 @@ and fun_decl = {
block : block reg option;
kwd_with : kwd_with option;
return : expr;
terminator : semi option
}
and fun_decl = {
fun_expr : fun_expr reg ;
terminator : semi option ;
}
and parameters = (param_decl, semi) nsepseq par reg
@ -266,12 +270,12 @@ and statement =
| Data of data_decl
and local_decl =
LocalFun of fun_decl reg
| LocalData of data_decl
and data_decl =
LocalConst of const_decl reg
| LocalVar of var_decl reg
| LocalFun of fun_decl reg
and var_decl = {
kwd_var : kwd_var;
@ -464,6 +468,7 @@ and expr =
| EUnit of c_Unit
| ETuple of tuple_expr
| EPar of expr par reg
| EFun of fun_expr reg
and annot_expr = (expr * type_expr)
@ -644,7 +649,8 @@ let rec expr_to_region = function
| EUnit region
| ECase {region;_}
| ECond {region; _}
| EPar {region; _} -> region
| EPar {region; _}
| EFun {region; _} -> region
and tuple_expr_to_region {region; _} = region
@ -752,7 +758,7 @@ let pattern_to_region = function
| PTuple {region; _} -> region
let local_decl_to_region = function
LocalFun {region; _}
| LocalData LocalFun {region; _}
| LocalData LocalConst {region; _}
| LocalData LocalVar {region; _} -> region

View File

@ -201,9 +201,9 @@ and type_tuple = (type_expr, comma) nsepseq par reg
(* Function declarations *)
and fun_decl ={
and fun_expr = {
kwd_function : kwd_function;
name : variable;
name : variable option;
param : parameters;
colon : colon;
ret_type : type_expr;
@ -212,7 +212,12 @@ and fun_decl ={
block : block reg option;
kwd_with : kwd_with option;
return : expr;
terminator : semi option }
}
and fun_decl = {
fun_expr : fun_expr reg ;
terminator : semi option ;
}
and parameters = (param_decl, semi) nsepseq par reg
@ -256,12 +261,12 @@ and statement =
| Data of data_decl
and local_decl =
LocalFun of fun_decl reg
| LocalData of data_decl
and data_decl =
LocalConst of const_decl reg
| LocalVar of var_decl reg
| LocalFun of fun_decl reg
and var_decl = {
kwd_var : kwd_var;
@ -454,6 +459,7 @@ and expr =
| EUnit of c_Unit
| ETuple of tuple_expr
| EPar of expr par reg
| EFun of fun_expr reg
and annot_expr = (expr * type_expr)

View File

@ -239,17 +239,11 @@ field_decl:
and value = {field_name = $1; colon = $2; field_type = $3}
in {region; value} }
(* Function declarations *)
fun_decl:
Function fun_name parameters COLON type_expr Is
seq(local_decl)
fun_expr:
Function option(fun_name) parameters COLON type_expr Is
block
With expr option(SEMI) {
let stop =
match $11 with
Some region -> region
| None -> expr_to_region $10 in
With expr {
let stop = expr_to_region $9 in
let region = cover $1 stop
and value = {
kwd_function = $1;
@ -258,18 +252,15 @@ fun_decl:
colon = $4;
ret_type = $5;
kwd_is = $6;
local_decls = $7;
block = Some $8;
kwd_with = Some $9;
return = $10;
terminator = $11}
local_decls = [];
block = Some $7;
kwd_with = Some $8;
return = $9;
}
in {region;value}}
| Function fun_name parameters COLON type_expr Is
expr option(SEMI) {
let stop =
match $8 with
Some region -> region
| None -> expr_to_region $7 in
| Function option(fun_name) parameters COLON type_expr Is
expr {
let stop = expr_to_region $7 in
let region = cover $1 stop
and value = {
kwd_function = $1;
@ -282,10 +273,36 @@ fun_decl:
block = None;
kwd_with = None;
return = $7;
terminator = $8;
}
in {region;value}}
(* Function declarations *)
fun_decl:
fun_expr option(SEMI) {
let stop =
match $2 with
Some region -> region
| None -> $1.region in
let region = cover $1.region stop
and value = {
fun_expr = $1;
terminator = $2;
}
in {region;value}}
open_fun_decl:
fun_expr {
let region = $1.region
and value = {
fun_expr = $1;
terminator = None;
}
in {region;value}}
parameters:
par(nsepseq(param_decl,SEMI)) { $1 }
@ -341,6 +358,7 @@ statement:
open_data_decl:
open_const_decl { LocalConst $1 }
| open_var_decl { LocalVar $1 }
| open_fun_decl { LocalFun $1 }
open_const_decl:
Const unqualified_decl(EQ) {
@ -370,13 +388,13 @@ open_var_decl:
terminator = None}
in {region; value}}
local_decl:
fun_decl { LocalFun $1 }
| data_decl { LocalData $1 }
/* local_decl: */
/* fun_decl { LocalFun $1 } */
/* | data_decl { LocalData $1 } */
data_decl:
const_decl { LocalConst $1 }
| var_decl { LocalVar $1 }
/* data_decl: */
/* const_decl { LocalConst $1 } */
/* | var_decl { LocalVar $1 } */
unqualified_decl(OP):
var COLON type_expr OP expr {
@ -390,12 +408,12 @@ const_decl:
}
| open_const_decl { $1 }
var_decl:
open_var_decl SEMI {
let var_decl : AST.var_decl = $1.value in
{$1 with value = {var_decl with terminator = Some $2}}
}
| open_var_decl { $1 }
/* var_decl: */
/* open_var_decl SEMI { */
/* let var_decl : AST.var_decl = $1.value in */
/* {$1 with value = {var_decl with terminator = Some $2}} */
/* } */
/* | open_var_decl { $1 } */
instruction:
conditional { Cond $1 }
@ -683,6 +701,7 @@ expr:
case(expr) { ECase ($1 expr_to_region) }
| cond_expr { $1 }
| disj_expr { $1 }
| fun_expr { EFun $1 }
cond_expr:
If expr Then expr option(SEMI) Else expr {

View File

@ -156,12 +156,13 @@ and print_type_tuple buffer {value; _} =
print_nsepseq buffer "," print_type_expr inside;
print_token buffer rpar ")"
and print_fun_decl buffer {value; _} =
and print_fun_expr buffer {value; _} =
let {kwd_function; name; param; colon;
ret_type; kwd_is; local_decls;
block; kwd_with; return; terminator} = value in
block; kwd_with; return;} = value in
let anonymous_name = Region.wrap_ghost "#anonymous" in
print_token buffer kwd_function "function";
print_var buffer name;
print_var buffer @@ Simple_utils.Option.unopt ~default:anonymous_name name;
print_parameters buffer param;
print_token buffer colon ":";
print_type_expr buffer ret_type;
@ -173,7 +174,11 @@ and print_fun_decl buffer {value; _} =
print_token buffer kwd_with "with";
| None -> ();
print_expr buffer return;
print_terminator buffer terminator
and print_fun_decl buffer {value; _} =
let {fun_expr ; terminator;} = value in
print_fun_expr buffer fun_expr;
print_terminator buffer terminator;
and print_parameters buffer {value; _} =
let {lpar; inside; rpar} = value in
@ -225,12 +230,12 @@ and print_local_decls buffer sequence =
List.iter (print_local_decl buffer) sequence
and print_local_decl buffer = function
LocalFun decl -> print_fun_decl buffer decl
| LocalData decl -> print_data_decl buffer decl
and print_data_decl buffer = function
LocalConst decl -> print_const_decl buffer decl
| LocalVar decl -> print_var_decl buffer decl
| LocalFun decl -> print_fun_decl buffer decl
and print_var_decl buffer {value; _} =
let {kwd_var; name; colon; var_type;
@ -402,6 +407,7 @@ and print_expr buffer = function
| EUnit r -> print_token buffer r "Unit"
| ETuple e -> print_tuple_expr buffer e
| EPar e -> print_par_expr buffer e
| EFun e -> print_fun_expr buffer e
and print_annot_expr buffer (expr , type_expr) =
print_expr buffer expr;
@ -795,7 +801,7 @@ and pp_declaration buffer ~pad:(_,pc as pad) = function
pp_const_decl buffer ~pad value
| FunDecl {value; region} ->
pp_loc_node buffer ~pad "FunDecl" region;
pp_fun_decl buffer ~pad value
pp_fun_expr buffer ~pad value.fun_expr.value
and pp_const_decl buffer ~pad:(_,pc) decl =
pp_ident buffer ~pad:(mk_pad 3 0 pc) decl.name;
@ -861,12 +867,13 @@ and pp_type_tuple buffer ~pad:(_,pc) {value; _} =
pp_type_expr buffer ~pad:(mk_pad len rank pc)
in List.iteri (List.length components |> apply) components
and pp_fun_decl buffer ~pad:(_,pc) decl =
and pp_fun_expr buffer ~pad:(_,pc) decl =
let fields =
if decl.local_decls = [] then 5 else 6 in
let () =
let pad = mk_pad fields 0 pc in
pp_ident buffer ~pad decl.name in
let anonymous_name = Region.wrap_ghost "#anonymous" in
pp_ident buffer ~pad @@ Simple_utils.Option.unopt ~default:anonymous_name decl.name in
let () =
let pad = mk_pad fields 1 pc in
pp_node buffer ~pad "<parameters>";
@ -1294,9 +1301,6 @@ and pp_local_decls buffer ~pad:(_,pc) decls =
in List.iteri (List.length decls |> apply) decls
and pp_local_decl buffer ~pad:(_,pc as pad) = function
LocalFun {value; region} ->
pp_loc_node buffer ~pad "LocalFun" region;
pp_fun_decl buffer ~pad value
| LocalData data ->
pp_node buffer ~pad "LocalData";
pp_data_decl buffer ~pad:(mk_pad 1 0 pc) data
@ -1308,6 +1312,9 @@ and pp_data_decl buffer ~pad = function
| LocalVar {value; region} ->
pp_loc_node buffer ~pad "LocalVar" region;
pp_var_decl buffer ~pad value
| LocalFun {value; region} ->
pp_loc_node buffer ~pad "LocalFun" region;
pp_fun_expr buffer ~pad value.fun_expr.value
and pp_var_decl buffer ~pad:(_,pc) decl =
pp_ident buffer ~pad:(mk_pad 3 0 pc) decl.name;
@ -1368,6 +1375,9 @@ and pp_expr buffer ~pad:(_,pc as pad) = function
| EPar {value; region} ->
pp_loc_node buffer ~pad "EPar" region;
pp_expr buffer ~pad:(mk_pad 1 0 pc) value.inside
| EFun {value; region} ->
pp_loc_node buffer ~pad "EFun" region;
pp_fun_expr ~pad buffer value;
and pp_list_expr buffer ~pad:(_,pc as pad) = function
ECons {value; region} ->

View File

@ -117,6 +117,22 @@ module Errors = struct
] in
error ~data title message
let unexpected_anonymous_function loc =
let title () = "unexpected anonymous function" in
let message () = "you provided a function declaration without name" in
let data = [
("loc" , fun () -> Format.asprintf "%a" Location.pp @@ loc)
] in
error ~data title message
let unexpected_named_function loc =
let title () = "unexpected named function" in
let message () = "you provided a function expression with a name (remove it)" in
let data = [
("loc" , fun () -> Format.asprintf "%a" Location.pp @@ loc)
] in
error ~data title message
(* Logging *)
let simplifying_instruction t =
@ -410,6 +426,13 @@ let rec simpl_expression (t:Raw.expr) : expr result =
let%bind index = simpl_expression lu.index.value.inside in
return @@ e_look_up ~loc path index
)
| EFun f -> (
let (f , loc) = r_split f in
let%bind ((name_opt , _ty_opt) , f') = simpl_fun_expression ~loc f in
match name_opt with
| None -> return @@ f'
| Some _ -> fail @@ unexpected_named_function loc
)
and simpl_logic_expression (t:Raw.logic_expr) : expression result =
let return x = ok x in
@ -497,10 +520,6 @@ and simpl_local_declaration : Raw.local_decl -> _ result = fun t ->
match t with
| LocalData d ->
simpl_data_declaration d
| LocalFun f ->
let (f , loc) = r_split f in
let%bind (name , e) = simpl_fun_declaration ~loc f in
return_let_in ~loc name e
and simpl_data_declaration : Raw.data_decl -> _ result = fun t ->
match t with
@ -516,6 +535,11 @@ and simpl_data_declaration : Raw.data_decl -> _ result = fun t ->
let%bind t = simpl_type_expression x.const_type in
let%bind expression = simpl_expression x.init in
return_let_in ~loc (name , Some t) expression
| LocalFun f ->
let (f , loc) = r_split f in
let%bind ((name_opt , ty_opt) , e) = simpl_fun_expression ~loc f.fun_expr.value in
let%bind name = trace_option (unexpected_anonymous_function loc) name_opt in
return_let_in ~loc (name , ty_opt) e
and simpl_param : Raw.param_decl -> (type_name * type_expression) result =
fun t ->
@ -531,11 +555,11 @@ and simpl_param : Raw.param_decl -> (type_name * type_expression) result =
let%bind type_expression = simpl_type_expression c.param_type in
ok (type_name , type_expression)
and simpl_fun_declaration :
loc:_ -> Raw.fun_decl -> ((name * type_expression option) * expression) result =
and simpl_fun_expression :
loc:_ -> Raw.fun_expr -> ((name option * type_expression option) * expression) result =
fun ~loc x ->
let open! Raw in
let {name;param;ret_type;local_decls;block;return} : fun_decl = x in
let {name;param;ret_type;local_decls;block;return} : fun_expr = x in
let statements =
match block with
| Some block -> npseq_to_list block.value.statements
@ -544,7 +568,7 @@ and simpl_fun_declaration :
(match param.value.inside with
a, [] -> (
let%bind input = simpl_param a in
let name = name.value in
let name = Option.map (fun (x : _ reg) -> x.value) name in
let (binder , input_type) = input in
let%bind local_declarations =
bind_map_list simpl_local_declaration local_decls in
@ -591,7 +615,8 @@ and simpl_fun_declaration :
let expression =
e_lambda ~loc binder (Some input_type) (Some output_type) result in
let type_annotation = Some (T_function (input_type, output_type)) in
ok ((name.value , type_annotation) , expression)
let name = Option.map (fun (x : _ reg) -> x.value) name in
ok ((name , type_annotation) , expression)
)
)
and simpl_declaration : Raw.declaration -> declaration Location.wrap result =
@ -614,7 +639,8 @@ and simpl_declaration : Raw.declaration -> declaration Location.wrap result =
bind_map_location simpl_const_decl (Location.lift_region x)
| FunDecl x -> (
let (x , loc) = r_split x in
let%bind ((name , ty_opt) , expr) = simpl_fun_declaration ~loc x in
let%bind ((name_opt , ty_opt) , expr) = simpl_fun_expression ~loc x.fun_expr.value in
let%bind name = trace_option (unexpected_anonymous_function loc) name_opt in
ok @@ Location.wrap ~loc (Declaration_constant (name , ty_opt , expr))
)

View File

@ -2,8 +2,8 @@ type storage_ is big_map(int, int) * unit
type foo is big_map(int, int)
function main(const p : unit; const s : storage_) : list(operation) * storage_ is
var toto : option (int) := Some(0);
block {
var toto : option (int) := Some(0);
toto := s.0[23];
s.0[2] := 444;
}

View File

@ -1,4 +1,5 @@
function foo (const i : int) : int is
block {
function bar (const j : int) : int is
block { skip } with i + j ;
block { skip } with bar (i)
i + j ;
} with bar (i)

View File

@ -1,5 +1,6 @@
function foobar(const i : int) : int is
block {
const j : int = 3 ;
function toto(const k : int) : int is
block { skip } with i + j + k ;
block { skip } with toto(42)
i + j + k ;
} with toto(42)

View File

@ -3,8 +3,10 @@
// https://gitlab.com/ligolang/ligo/commit/faf3bbc06106de98189f1c1673bd57e78351dc7e
function foobar(const i : int) : int is
block {
const j : int = 3 ;
const k : int = 4 ;
function toto(const l : int) : int is
block { skip } with i + j + k + l;
block { skip } with toto(42)
i + j + k + l;
} with toto(42)

View File

@ -1,6 +1,7 @@
function toto (const i : int) : int is
block {
function tata (const j : int) : int is
block { skip } with i + j ;
i + j ;
function titi (const j : int) : int is
block { skip } with i + j ;
block { skip } with tata(i) + titi(i)
i + j ;
} with tata(i) + titi(i)

View File

@ -1,8 +1,8 @@
// Test if conditional in PascaLIGO
function main (const i : int) : int is
var result : int := 23 ;
begin
var result : int := 23 ;
if i = 2 then
result := 42
else
@ -10,7 +10,7 @@ function main (const i : int) : int is
end with result
function foo (const b : bool) : int is
var x : int := 41 ;
begin
var x : int := 41 ;
x := 1 + (if b then x else main(x)) ;
end with x

View File

@ -3,20 +3,17 @@ type pii is (int*int)
type ppi is record x:pii; y:pii end
type ppp is (ppi*ppi)
function main (const toto : unit) : int is
var a : ppp :=
(
record
x = (0,1);
y = (10,11);
end
,
record
x = (100,101);
y = (110,111);
end
)
begin
function main (const toto : unit) : int is block {
var a : ppp := (
record
x = (0,1);
y = (10,11);
end ,
record
x = (100,101);
y = (110,111);
end
) ;
a.0.x.0 := 2;
const b:int = a.0.x.0;
end with b
} with b

View File

@ -12,8 +12,8 @@ function main (const p : param; const s : unit) : list(operation) * unit is
with ((nil : list(operation)), s)
function foobar (const i : int) : int is
var p : param := Zero (42n) ;
block {
var p : param := Zero (42n) ;
if i > 0 then block {
i := i + 1 ;
if i > 10 then block {

View File

@ -1,9 +1,9 @@
// Test a PascaLIGO function with more complex logic than function.ligo
function main (const i : int) : int is
begin
var j : int := 0 ;
var k : int := 1 ;
begin
j := k + i ;
k := i + j ;
end with (k + j)

View File

@ -1,10 +1,11 @@
// Test a PascaLIGO function which takes another PascaLIGO function as an argument
function foobar (const i : int) : int is
block {
function foo (const i : int) : int is
block { skip } with i ;
i ;
function bar (const f : int -> int) : int is
block { skip } with f ( i ) ;
block { skip } with bar (foo) ;
f ( i ) ;
} with bar (foo) ;
// higher order function with more than one argument
function higher2(const i: int; const f: int -> int): int is
@ -13,27 +14,26 @@ function higher2(const i: int; const f: int -> int): int is
} with ii
function foobar2 (const i : int) : int is
block {
function foo2 (const i : int) : int is
block { skip } with i;
block { skip } with higher2(i,foo2)
i;
} with higher2(i,foo2)
const a : int = 0;
function foobar3 (const i : int) : int is
block {
function foo2 (const i : int) : int is
block { skip } with (a+i);
block { skip } with higher2(i,foo2)
(a+i);
} with higher2(i,foo2)
function f (const i : int) : int is
block { skip }
with i
i
function g (const i : int) : int is
block { skip }
with f(i)
f(i)
function foobar4 (const i : int) : int is
block { skip }
with g(g(i))
g(g(i))
function higher3(const i: int; const f: int -> int; const g: int -> int): int is
block {
@ -41,9 +41,10 @@ function higher3(const i: int; const f: int -> int; const g: int -> int): int is
} with ii
function foobar5 (const i : int) : int is
block {
const a : int = 0;
function foo (const i : int) : int is
block { skip } with (a+i);
(a+i);
function goo (const i : int) : int is
block { skip } with foo(i);
block { skip } with higher3(i,foo,goo)
foo(i);
} with higher3(i,foo,goo)

View File

@ -2,5 +2,6 @@ function f (const x : unit) : unit is
begin skip end with unit
function main (const p : unit ; const s : unit) : unit is
begin
var y : unit := f(unit) ;
begin skip end with y
end with y

View File

@ -25,15 +25,16 @@ const bl : foobar = list
end
function iter_op (const s : list(int)) : int is
begin
var r : int := 0 ;
function aggregate (const i : int) : unit is
begin
r := r + i ;
end with unit
begin
end with unit ;
list_iter(s , aggregate) ;
end with r
function map_op (const s : list(int)) : list(int) is
block {
function increment (const i : int) : int is block { skip } with i + 1
block { skip } with list_map(s , increment)
} with list_map(s , increment)

View File

@ -48,23 +48,25 @@ function get_ (const m : foobar) : option(int) is
end with map_get(42 , m)
function iter_op (const m : foobar) : unit is
block {
function aggregate (const i : int ; const j : int) : unit is block
{ if (i=j) then skip else failwith("fail") } with unit ;
block {skip}
// map_iter(m , aggregate) ;
with map_iter(m, aggregate) ;
} with map_iter(m, aggregate) ;
function map_op (const m : foobar) : foobar is
block {
function increment (const i : int ; const j : int) : int is block { skip } with j + 1 ;
block { skip } with map_map(m , increment) ;
} with map_map(m , increment) ;
function fold_op (const m : foobar) : int is
block {
function aggregate (const i : int ; const j : (int * int)) : int is block { skip } with i + j.0 + j.1 ;
block { skip } with map_fold(m , 10 , aggregate)
} with map_fold(m , 10 , aggregate)
function deep_op (var m : foobar) : foobar is
var coco : (int*foobar) := (0, m);
block {
block {
var coco : (int*foobar) := (0, m);
remove 42 from map coco.1 ;
coco.1[32] := 16 ;
} with coco.1
} with coco.1

View File

@ -1,8 +1,8 @@
// Test the pattern matching functionality of PascaLIGO
function match_bool (const i : int) : int is
var result : int := 23 ;
begin
var result : int := 23 ;
case i = 2 of
| True -> result := 42
| False -> result := 0
@ -10,8 +10,8 @@ function match_bool (const i : int) : int is
end with result
function match_option (const o : option(int)) : int is
var result : int := 23 ;
begin
var result : int := 23 ;
case o of
| None -> skip
| Some (s) -> result := s
@ -19,21 +19,18 @@ function match_option (const o : option(int)) : int is
end with result
function match_expr_bool (const i : int) : int is
begin skip end with
case i = 2 of
| True -> 42
| False -> 0
end
function match_expr_option (const o : option(int)) : int is
begin skip end with
case o of
| None -> 42
| Some (s) -> s
end
function match_expr_list (const l : list(int)) : int is
begin skip end with
case l of
| nil -> -1
| hd # tl -> hd

View File

@ -6,10 +6,8 @@ const s : foobar = Some(42)
const n : foobar = None
function assign (var m : int) : foobar is
block {
var coco : foobar := None;
block
{
coco := Some(m);
coco := None;
}
with coco
} with coco

View File

@ -1,16 +1,17 @@
// Test set iteration in PascaLIGO
function iter_op (const s : set(int)) : int is
begin
var r : int := 0 ;
function aggregate (const i : int) : unit is
begin
r := r + i ;
end with unit
begin
end with unit ;
set_iter(s , aggregate) ;
end with r
function fold_op (const s : set(int)) : int is
block {
function aggregate (const i : int ; const j : int) : int is
block { skip } with i + j
block { skip } with set_fold(s , 15 , aggregate)
i + j
} with set_fold(s , 15 , aggregate)

View File

@ -1,4 +1,5 @@
function foo (const i : int) : int is
block {
function bar (const i : int) : int is
block { skip } with i ;
block { skip } with bar (0)
i ;
} with bar (0)

View File

@ -7,10 +7,10 @@ end
type mpi is map(string,int)
function main (const toto : tpi) : int is
begin
var a : tpi := toto;
var b : rpi := record x = 0; y=1 ; end;
var m : mpi := map "y" -> 1; end;
begin
a.0 := 2;
b.x := a.0;
m["x"] := b.x;

View File

@ -108,6 +108,8 @@ let make ~(start: Pos.t) ~(stop: Pos.t) =
let ghost = make ~start:Pos.ghost ~stop:Pos.ghost
let wrap_ghost value = {value ; region = ghost}
let min = make ~start:Pos.min ~stop:Pos.min
(* Comparisons *)

View File

@ -96,6 +96,11 @@ val make : start:Pos.t -> stop:Pos.t -> t
val ghost : t (* Two [Pos.ghost] positions *)
(* This wraps a value with a ghost region. *)
val wrap_ghost : 'a -> 'a reg
(* Occasionnally, we may need a minimum region. It is here made of two
minimal positions. *)