Skip to content

Commit ac42e2a

Browse files
strubfdupress
authored andcommitted
subtype: generalize over section-declared free types at section close
[subtype X = { x : T | P x }] declared inside [section. declare type c. ...] previously stayed monomorphic at section close. The cloned auto-axioms ([insubN], [insubT], [valP], [valK]) and the [val] / [insub] operators correctly gained a [c] tparam, but [X] itself didn't — the result was an inconsistent state where the operations were polymorphic over [c] but returned a single shared [X] type for every instantiation, a soundness gap flagged by [FIXME:SUBTYPE] at [add_subtype]. This patch threads the carrier and predicate through the subtype's [tydecl] so [tydecl_fv] picks up their dependency on section-declared free types and the existing [generalize_tydecl] machinery adds the right tparams at close. Tests in [tests/subtype-section-generalize.ec] cover three cases: 1. carrier mentions a section-declared free type — subtype is unary. 2. predicate mentions a section-declared free type (carrier doesn't) — same outcome via fv collected from the predicate. 3. nested sections, subtype declared in the inner one, depending on free types from both levels — subtype is binary after both closes. Each test instantiates [val] and [valP] at two distinct carriers in a single lemma; would fail to type-check if the cloned type or axiom had stayed monomorphic.
1 parent dda4571 commit ac42e2a

8 files changed

Lines changed: 154 additions & 19 deletions

File tree

src/ecDecl.ml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ type ty_body =
3535

3636

3737
type tydecl = {
38-
tyd_params : ty_params;
39-
tyd_type : ty_body;
40-
tyd_loca : locality;
38+
tyd_params : ty_params;
39+
tyd_type : ty_body;
40+
tyd_loca : locality;
41+
tyd_subtype : (EcTypes.ty * EcCoreFol.form) option;
4142
}
4243

4344
let tydecl_as_concrete (td : tydecl) =
@@ -65,7 +66,10 @@ let abs_tydecl ?(params = `Int 0) lc =
6566
(EcUid.NameGen.bulk ~fmt n)
6667
in
6768

68-
{ tyd_params = params; tyd_type = Abstract; tyd_loca = lc; }
69+
{ tyd_params = params;
70+
tyd_type = Abstract;
71+
tyd_loca = lc;
72+
tyd_subtype = None; }
6973

7074
(* -------------------------------------------------------------------- *)
7175
let ty_instantiate (params : ty_params) (args : ty list) (ty : ty) =

src/ecDecl.mli

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,18 @@ type ty_body =
3030

3131

3232
type tydecl = {
33-
tyd_params : ty_params;
34-
tyd_type : ty_body;
35-
tyd_loca : locality;
33+
tyd_params : ty_params;
34+
tyd_type : ty_body;
35+
tyd_loca : locality;
36+
(* For [subtype]-declared types: the carrier and the predicate. The
37+
declared type itself stays [tyd_type = Abstract], because a
38+
subtype is semantically a fresh abstract type — but its dependency
39+
on free type variables (when declared inside a section) must be
40+
visible to the section-close machinery. [tydecl_fv] unions the
41+
carrier+predicate fv into the type's fv when this field is set,
42+
so a subtype declared inside [section. declare type c.] gets the
43+
section's tparams added at close, just like type aliases do. *)
44+
tyd_subtype : (EcTypes.ty * EcCoreFol.form) option;
3645
}
3746

3847
val tydecl_as_concrete : tydecl -> EcTypes.ty option

src/ecHiInductive.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ let trans_datatype (env : EcEnv.env) (name : ptydname) (dt : pdatatype) =
8686
tyd_params = EcUnify.UniEnv.tparams ue;
8787
tyd_type = Abstract;
8888
tyd_loca = lc;
89+
tyd_subtype = None;
8990
} in
9091
EcEnv.Ty.bind (unloc name) myself env
9192
in

src/ecScope.ml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2296,20 +2296,14 @@ module Ty = struct
22962296
record.ELI.rc_tparams, Record (scheme, record.ELI.rc_fields)
22972297
in
22982298

2299-
bind scope (unloc name, { tyd_params; tyd_type; tyd_loca; })
2299+
bind scope (unloc name,
2300+
{ tyd_params; tyd_type; tyd_loca; tyd_subtype = None; })
23002301

23012302
(* ------------------------------------------------------------------ *)
23022303
let add_subtype (scope : scope) ({ pl_desc = subtype } : psubtype located) =
23032304
let loced x = mk_loc _dummy x in
23042305
let env = env scope in
23052306

2306-
let scope =
2307-
let decl = EcDecl.{
2308-
tyd_params = [];
2309-
tyd_type = Abstract;
2310-
tyd_loca = `Global; (* FIXME:SUBTYPE *)
2311-
} in bind scope (unloc subtype.pst_name, decl) in
2312-
23132307
let carrier =
23142308
let ue = EcUnify.UniEnv.create None in
23152309
transty tp_tydecl env ue subtype.pst_carrier in
@@ -2326,6 +2320,14 @@ module Ty = struct
23262320
let fs = Tuni.subst uidmap in
23272321
f_lambda [(x, GTty carrier)] (Fsubst.f_subst fs pred) in
23282322

2323+
let scope =
2324+
let decl = EcDecl.{
2325+
tyd_params = [];
2326+
tyd_type = Abstract;
2327+
tyd_loca = `Global;
2328+
tyd_subtype = Some (carrier, pred);
2329+
} in bind scope (unloc subtype.pst_name, decl) in
2330+
23292331
let evclone =
23302332
{ EcThCloning.evc_empty with
23312333
evc_types = Msym.of_list [

src/ecSection.ml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,13 @@ let tydecl_fv tyd =
730730
| Record (_f, l) ->
731731
List.fold_left (fun fv (_, ty) ->
732732
EcIdent.fv_union fv (ty_fv_and_tvar ty)) Mid.empty l in
733+
let fv =
734+
match tyd.tyd_subtype with
735+
| None -> fv
736+
| Some (carrier, pred) ->
737+
EcIdent.fv_union
738+
(EcIdent.fv_union fv (ty_fv_and_tvar carrier))
739+
(fv_and_tvar_f pred) in
733740
List.fold_left (fun fv id -> Mid.remove id fv) fv tyd.tyd_params
734741

735742
let op_body_fv body ty =
@@ -863,7 +870,8 @@ let generalize_tydecl to_gen prefix (name, tydecl) =
863870
let to_gen = { to_gen with tg_subst} in
864871
let tydecl = {
865872
tyd_params; tyd_type;
866-
tyd_loca = `Global; } in
873+
tyd_loca = `Global;
874+
tyd_subtype = tydecl.tyd_subtype; } in
867875
to_gen, Some (Th_type (name, tydecl))
868876

869877
| `Declare ->

src/ecSubst.ml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,10 +868,15 @@ let subst_tydecl_body (s : subst) (tyd : ty_body) =
868868
let subst_tydecl (s : subst) (tyd : tydecl) =
869869
let s, tparams = fresh_tparams s tyd.tyd_params in
870870
let body = subst_tydecl_body s tyd.tyd_type in
871+
let subtype =
872+
Option.map
873+
(fun (carrier, pred) -> (subst_ty s carrier, subst_form s pred))
874+
tyd.tyd_subtype in
871875

872876
{ tyd_params = tparams;
873877
tyd_type = body;
874-
tyd_loca = tyd.tyd_loca; }
878+
tyd_loca = tyd.tyd_loca;
879+
tyd_subtype = subtype; }
875880

876881
(* -------------------------------------------------------------------- *)
877882
let rec subst_op_kind (s : subst) (kind : operator_kind) =

src/ecTheoryReplay.ml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,8 @@ let rec replay_tyd (ove : _ ovrenv) (subst, ops, proofs, scope) (import, x, otyd
431431
let decl =
432432
{ tyd_params = nargs;
433433
tyd_type = Concrete ntyd;
434-
tyd_loca = otyd.tyd_loca; }
434+
tyd_loca = otyd.tyd_loca;
435+
tyd_subtype = None; }
435436

436437
in (decl, ntyd)
437438

@@ -451,7 +452,8 @@ let rec replay_tyd (ove : _ ovrenv) (subst, ops, proofs, scope) (import, x, otyd
451452
let decl =
452453
{ tyd_params = [];
453454
tyd_type = Concrete ty;
454-
tyd_loca = otyd.tyd_loca; }
455+
tyd_loca = otyd.tyd_loca;
456+
tyd_subtype = None; }
455457

456458
in (decl, ty)
457459
end
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
require import AllCore.
2+
require Subtype.
3+
4+
(* Generic abstract predicates declared outside any section so they
5+
stay polymorphic instead of being generalized when the section is
6+
closed. *)
7+
op P ['c] : int -> 'c -> bool.
8+
op Q ['c] : int -> 'c -> bool.
9+
op R ['o 'i] : 'o -> 'i -> bool.
10+
11+
axiom P_sat ['c] : exists n, P<:'c> n witness.
12+
axiom Q_sat ['c] : exists n, Q<:'c> n witness.
13+
axiom R_sat ['o 'i] : R<:'o, 'i> witness witness.
14+
15+
16+
(* -------------------------------------------------------------------- *)
17+
(* Basic case: subtype inside a section with a single section-declared *)
18+
(* free type. The subtype must be generalized over [c] at section *)
19+
(* close so that [val]/[insub] (which gain a [c] tparam) and the type *)
20+
(* itself stay coherent. *)
21+
(* -------------------------------------------------------------------- *)
22+
theory T1.
23+
section.
24+
declare type c.
25+
26+
subtype carrier = { x : int * c | P x.`1 x.`2 }.
27+
28+
realize inhabited. proof. by have [n hn] := P_sat<:c>; exists (n, witness). qed.
29+
30+
end section.
31+
end T1.
32+
33+
(* After section close, [carrier] is unary. Using it at two distinct
34+
carrier types in the same lemma produces two distinct types — would
35+
fail to type-check if [carrier] had stayed monomorphic. *)
36+
lemma test_basic_unary (x : bool T1.carrier) (y : int T1.carrier) :
37+
T1.val x = (0, witness<:bool>) /\ T1.val y = (1, witness<:int>).
38+
proof. admit. qed.
39+
40+
41+
(* -------------------------------------------------------------------- *)
42+
(* Predicate-only dependency: the carrier mentions no section-declared *)
43+
(* type, but the predicate does (the polymorphic [Q] is instantiated at *)
44+
(* the section's [c]). The fv collected from the predicate must trigger *)
45+
(* the same generalization. *)
46+
(* -------------------------------------------------------------------- *)
47+
theory T2.
48+
section.
49+
declare type c.
50+
51+
subtype only_pred = { x : int | Q<:c> x witness }.
52+
53+
realize inhabited. proof. exact: Q_sat. qed.
54+
55+
end section.
56+
end T2.
57+
58+
lemma test_pred_dep_unary (x : bool T2.only_pred) (y : int T2.only_pred) :
59+
T2.val x = 0 /\ T2.val y = 1.
60+
proof.
61+
(* The subtype-cloned [valP] axiom must itself be polymorphic over the
62+
carrier — instantiating it at two distinct types here would fail to
63+
type-check if the generalization had not happened. *)
64+
have hx : Q<:bool> (T2.val x) witness by exact: T2.valP.
65+
have hy : Q<:int> (T2.val y) witness by exact: T2.valP.
66+
admit.
67+
qed.
68+
69+
70+
(* -------------------------------------------------------------------- *)
71+
(* Two nested sections. The subtype is declared in the inner section *)
72+
(* and depends on free types from BOTH levels. After both closes, it *)
73+
(* must be generalized over both [outer] and [inner]. *)
74+
(* -------------------------------------------------------------------- *)
75+
theory T3.
76+
section Outer.
77+
declare type outer.
78+
79+
section Inner.
80+
declare type inner.
81+
82+
subtype pair_carrier = { x : outer * inner | R x.`1 x.`2 }.
83+
84+
realize inhabited.
85+
proof. by exists (witness, witness); exact: R_sat. qed.
86+
87+
end section Inner.
88+
end section Outer.
89+
end T3.
90+
91+
(* Now [pair_carrier] should be binary. *)
92+
lemma test_nested_binary
93+
(x : (int, bool) T3.pair_carrier)
94+
(y : (bool, int) T3.pair_carrier)
95+
:
96+
T3.val x = (0, true) /\ T3.val y = (true, 0).
97+
proof.
98+
(* As in T2, instantiating [valP] at two distinct carrier pairs would
99+
fail to type-check if the cloned axiom had not been generalized
100+
over both [outer] and [inner]. *)
101+
have hx : R<:int, bool> (T3.val x).`1 (T3.val x).`2 by exact: T3.valP.
102+
have hy : R<:bool, int> (T3.val y).`1 (T3.val y).`2 by exact: T3.valP.
103+
admit.
104+
qed.

0 commit comments

Comments
 (0)