Skip to content

Commit 99757c1

Browse files
committed
feat: positivity checker seems to work; needs more testing
1 parent f1e2059 commit 99757c1

File tree

8 files changed

+117
-29
lines changed

8 files changed

+117
-29
lines changed

src/ecHiInductive.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ type dterror =
2323
| DTE_TypeError of TT.tyerror
2424
| DTE_DuplicatedCtor of symbol
2525
| DTE_InvalidCTorType of symbol * TT.tyerror
26-
| DTE_NonPositive
26+
| DTE_NonPositive of symbol * ty
2727
| DTE_Empty
2828

2929
type fxerror =
@@ -52,7 +52,7 @@ let trans_record (env : EcEnv.env) (name : ptydname) (rc : precord) =
5252
Msym.odup unloc (List.map fst rc) |> oiter (fun (x, y) ->
5353
rcerror y.pl_loc env (RCE_DuplicatedField x.pl_desc));
5454

55-
(* Check for emptyness *)
55+
(* Check for emptiness *)
5656
if List.is_empty rc then
5757
rcerror loc env RCE_Empty;
5858

@@ -106,7 +106,7 @@ let trans_datatype (env : EcEnv.env) (name : ptydname) (dt : pdatatype) =
106106
dt |> List.map for1
107107
in
108108

109-
(* Check for emptyness *)
109+
(* Check for emptiness *)
110110
begin
111111
let rec isempty_n (ctors : (ty list) list) =
112112
List.for_all isempty_1 ctors

src/ecHiInductive.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type dterror =
1616
| DTE_TypeError of EcTyping.tyerror
1717
| DTE_DuplicatedCtor of symbol
1818
| DTE_InvalidCTorType of symbol * EcTyping.tyerror
19-
| DTE_NonPositive
19+
| DTE_NonPositive of symbol * EcTypes.ty
2020
| DTE_Empty
2121

2222
type fxerror =

src/ecInductive.ml

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,80 @@ let datatype_ind_path (mode : indmode) (p : EcPath.path) =
8383
EcPath.pqoname (EcPath.prefix p) name
8484

8585
(* -------------------------------------------------------------------- *)
86-
exception NonPositive
87-
88-
let indsc_of_datatype ?normty (mode : indmode) (dt : datatype) =
89-
let normty = odfl (identity : ty -> ty) normty in
86+
exception NonPositive of ty
87+
exception NonPositiveAbstract of EcPath.path
88+
exception NonPositiveParams of ty
89+
90+
(** below, [fct] designates the function that takes a path to a type constructor
91+
and returns the corresponding type declaration *)
92+
(** Strict positivity enforces the following, for every variant of the datatype p:
93+
- for each subterm (a → b), p ∉ fv(a);
94+
- inductive occurences a₁ a₂ .. aₙ p are such that ∀i. p ∉ fv(aᵢ) *)
95+
96+
let rec occurs ?(normty = identity) p t =
97+
match (normty t).ty_node with
98+
| Tconstr (p', _) when EcPath.p_equal p p' -> true
99+
| _ -> EcTypes.ty_sub_exists (occurs p) t
100+
101+
let ty_params_compat =
102+
List.for_all2 (fun ty (param_id, _) ->
103+
match ty.ty_node with
104+
| Tvar id -> EcIdent.id_equal id param_id
105+
| _ -> false)
106+
107+
let rec check_positivity_in_decl fct p decl ident =
108+
let tys_to_inspect =
109+
match decl.tyd_type with
110+
| Concrete ty -> [ ty ]
111+
| Abstract _ -> raise @@ NonPositiveAbstract p
112+
| Datatype { tydt_ctors } -> List.flatten (List.map snd tydt_ctors)
113+
| Record (_, tys) -> List.map snd tys
114+
in
115+
List.iter (check_positivity_ident fct p decl.tyd_params ident) tys_to_inspect
116+
117+
(** Ensures all occurrences of type variable [ident] are positive in type [ty] *)
118+
and check_positivity_ident fct p params ident ty = match ty.ty_node with
119+
| Tglob _ | Tunivar _ -> assert false
120+
| Tvar _ -> ()
121+
| Ttuple tys -> List.iter (check_positivity_ident fct p params ident) tys
122+
| Tconstr (q, args) when EcPath.p_equal q p ->
123+
if not (ty_params_compat args params) then raise @@ NonPositiveParams ty
124+
| Tconstr (q, args) ->
125+
let decl = fct q in
126+
List.combine args decl.tyd_params
127+
|> List.filter_map
128+
(fun (arg, (ident, _)) -> if EcTypes.var_mem ident arg then Some ident else None)
129+
|> List.iter (check_positivity_in_decl fct q decl)
130+
| Tfun (from, to_) ->
131+
if EcTypes.var_mem ident from then raise @@ NonPositive ty;
132+
check_positivity_ident fct p params ident to_
133+
134+
135+
(** Ensures all occurrences of path [p] are positive in type [ty] *)
136+
let rec check_positivity_path fct p ty =
137+
match ty.ty_node with
138+
| Tglob _ | Tunivar _ -> assert false
139+
| Tvar _ -> ()
140+
| Ttuple tys -> List.iter (check_positivity_path fct p) tys
141+
| Tconstr (q, args) when EcPath.p_equal q p ->
142+
if List.exists (occurs p) args then raise (NonPositive ty)
143+
| Tconstr (q, args) ->
144+
let decl = fct q in
145+
List.combine args decl.tyd_params
146+
|> List.filter_map
147+
(fun (arg, (ident, _)) -> if occurs p arg then Some ident else None)
148+
|> List.iter (check_positivity_in_decl fct q decl)
149+
150+
| Tfun (from, to_) ->
151+
if occurs p from then raise (NonPositive ty);
152+
check_positivity_path fct p to_
153+
154+
let check_positivity fct dt =
155+
let tys = List.flatten (List.map snd dt.dt_ctors) in
156+
List.iter (check_positivity_path fct dt.dt_path) tys
157+
158+
159+
let indsc_of_datatype ?(normty = identity) (mode : indmode) (dt : datatype) =
90160
let tpath = dt.dt_path in
91161

92162
let rec scheme1 p (pred, fac) ty =
@@ -103,13 +173,11 @@ let indsc_of_datatype ?normty (mode : indmode) (dt : datatype) =
103173
| scs -> Some (FL.f_let (LTuple xs) fac (FL.f_ands scs))
104174
end
105175

106-
| Tconstr (p', ts) ->
107-
if List.exists (occurs p) ts then raise NonPositive;
176+
| Tconstr (p', _) ->
108177
if not (EcPath.p_equal p p') then None else
109178
Some (FL.f_app pred [fac] tbool)
110179

111180
| Tfun (ty1, ty2) ->
112-
if occurs p ty1 then raise NonPositive;
113181
let x = fresh_id_of_ty ty1 in
114182
scheme1 p (pred, FL.f_app fac [FL.f_local x ty1] ty2) ty2
115183
|> omap (FL.f_forall [x, GTty ty1])
@@ -152,11 +220,6 @@ let indsc_of_datatype ?normty (mode : indmode) (dt : datatype) =
152220
let form = FL.f_forall [predx, GTty predty] form in
153221
form
154222

155-
and occurs p t =
156-
match (normty t).ty_node with
157-
| Tconstr (p', _) when EcPath.p_equal p p' -> true
158-
| _ -> EcTypes.ty_sub_exists (occurs p) t
159-
160223
in scheme mode (List.map fst dt.dt_tparams, tpath) dt.dt_ctors
161224

162225
(* -------------------------------------------------------------------- *)

src/ecInductive.mli

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ type ctor = symbol * (EcTypes.ty list)
2121
type ctors = ctor list
2222

2323
type datatype = {
24-
dt_path : path;
25-
dt_tparams : ty_params;
26-
dt_ctors : ctors
24+
dt_path : path;
25+
dt_tparams : ty_params;
26+
dt_ctors : ctors
2727
}
2828

2929
(* -------------------------------------------------------------------- *)
@@ -43,7 +43,17 @@ val datatype_proj_name : symbol -> symbol
4343
val datatype_proj_path : path -> symbol -> path
4444

4545
(* -------------------------------------------------------------------- *)
46-
exception NonPositive
46+
(** A failure raised during a strict-positivity check. The companion type
47+
is the term responsible for the failure. *)
48+
exception NonPositive of ty
49+
50+
(** Evaluates whether a given datatype protype satisfies the strict
51+
positivity check. The first argument defines how to retrieve the
52+
effective definition of a type constructor from its path.
53+
54+
raises the exception [NonPositive] if the check fails, otherwise
55+
the function returns a unit value. *)
56+
val check_positivity : (path -> tydecl) -> datatype -> unit
4757

4858
val indsc_of_datatype : ?normty:(ty -> ty) -> [`Elim|`Case] -> datatype -> form
4959

src/ecScope.ml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,13 +1953,18 @@ module Ty = struct
19531953
let body = transty tp_tydecl env ue bd in
19541954
EcUnify.UniEnv.tparams ue, Concrete body
19551955

1956-
| PTYD_Datatype dt ->
1957-
let datatype = EHI.trans_datatype env (mk_loc loc (args,name)) dt in
1958-
let tparams, tydt =
1959-
try ELI.datatype_as_ty_dtype datatype
1960-
with ELI.NonPositive -> EHI.dterror loc env EHI.DTE_NonPositive
1961-
in
1962-
tparams, Datatype tydt
1956+
| PTYD_Datatype dt -> (
1957+
let datatype = EHI.trans_datatype env (mk_loc loc (args, name)) dt in
1958+
(* Maybe this is not _the_ one way to build it, compare to
1959+
ecHiInductive.ml#L132-L134 *)
1960+
let ty_from_ctor ctor = EcEnv.Ty.by_path ctor env in
1961+
try
1962+
ELI.check_positivity ty_from_ctor datatype;
1963+
let tparams, tydt = ELI.datatype_as_ty_dtype datatype in
1964+
(tparams, Datatype tydt)
1965+
with ELI.NonPositive ty ->
1966+
EHI.dterror loc env
1967+
(EHI.DTE_NonPositive (basename datatype.dt_path, ty)))
19631968

19641969
| PTYD_Record rt ->
19651970
let record = EHI.trans_record env (mk_loc loc (args,name)) rt in

src/ecTypes.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ let rec ty_check_uni t =
154154
| Tunivar _ -> raise FoundUnivar
155155
| _ -> ty_iter ty_check_uni t
156156

157+
let rec var_mem ?(check_glob = false) id t =
158+
match t.ty_node with
159+
| Tvar id' -> EcIdent.id_equal id id'
160+
| Tglob id' when check_glob -> EcIdent.id_equal id id'
161+
| _ -> ty_sub_exists (var_mem ~check_glob id) t
162+
157163
(* -------------------------------------------------------------------- *)
158164
let symbol_of_ty (ty : ty) =
159165
match ty.ty_node with

src/ecTypes.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ val ty_sub_exists : (ty -> bool) -> ty -> bool
7979
val ty_fold : ('a -> ty -> 'a) -> 'a -> ty -> 'a
8080
val ty_iter : (ty -> unit) -> ty -> unit
8181

82+
val var_mem : ?check_glob:bool -> EcIdent.t -> ty -> bool
83+
8284
(* -------------------------------------------------------------------- *)
8385
val symbol_of_ty : ty -> string
8486
val fresh_id_of_ty : ty -> EcIdent.t

src/ecUserMessages.ml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ end = struct
593593

594594
let pp_dterror env fmt error =
595595
let msg x = Format.fprintf fmt x in
596+
let env1 = EcPrinting.PPEnv.ofenv env in
596597

597598
match error with
598599
| DTE_TypeError ee ->
@@ -605,8 +606,9 @@ end = struct
605606
msg "invalid constructor type: `%s`: %a'"
606607
name (pp_tyerror env) ee
607608

608-
| DTE_NonPositive ->
609-
msg "the datatype does not respect the positivity condition"
609+
| DTE_NonPositive (name, ty) ->
610+
msg "non strictly-positive occurrence of type `%s` in `%a`"
611+
name (EcPrinting.pp_type env1) ty
610612

611613
| DTE_Empty ->
612614
msg "the datatype may be empty"

0 commit comments

Comments
 (0)