Skip to content

Commit cd77691

Browse files
committed
Factor code between records and typeclasses
This is a followup of #12611
1 parent 7d4974e commit cd77691

File tree

2 files changed

+110
-134
lines changed

2 files changed

+110
-134
lines changed

vernac/record.ml

Lines changed: 108 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ end
118118

119119
type projection_flags = {
120120
pf_subclass: bool;
121+
pf_priority: int option;
121122
pf_canonical: bool;
122123
}
123124

@@ -413,7 +414,7 @@ let build_proj env mib indsp primitive x rp lifted_fields ~poly paramdecls param
413414

414415
(** [declare_projections] prepares the common context for all record
415416
projections and then calls [build_proj] for each one. *)
416-
let declare_projections indsp univs ?(kind=Decls.StructureComponent) inhabitant_id flags fieldimpls fields =
417+
let declare_projections indsp univs ~kind inhabitant_id flags fieldimpls fields =
417418
let env = Global.env() in
418419
let (mib,mip) = Global.lookup_inductive indsp in
419420
let poly = Declareops.inductive_is_polymorphic mib in
@@ -528,7 +529,7 @@ let data_name id rdata =
528529
The entry point is [definition_structure], which will match on the
529530
declared [kind] and then either follow the regular record
530531
declaration path to [declare_structure] or handle the record as a
531-
class declaration with [declare_class].
532+
definitional class declaration with [declare_class_constant].
532533
533534
*)
534535

@@ -538,8 +539,8 @@ let data_name id rdata =
538539
- prepares and declares the corresponding record projections, mainly taken care of by
539540
[declare_projections]
540541
*)
541-
let declare_structure ~cumulative finite ~univs ~variances ~primitive_proj
542-
paramimpls params template ?(kind=Decls.StructureComponent) ?name (record_data : Data.t list) =
542+
let declare_structure ~cumulative finite ~univs ~variances ~primitive_proj ~proj_kind
543+
paramimpls params template (record_data : Data.t list) =
543544
let nparams = List.length params in
544545
let (univs, ubinders) = univs in
545546
let poly, projunivs =
@@ -592,20 +593,34 @@ let declare_structure ~cumulative finite ~univs ~variances ~primitive_proj
592593
let map i { Data.is_coercion; coers; rdata = { DataR.implfs; fields; _}; inhabitant_id; id; _ } =
593594
let rsp = (kn, i) in (* This is ind path of idstruc *)
594595
let cstr = (rsp, 1) in
595-
let projections = declare_projections rsp (projunivs,ubinders) ~kind inhabitant_id coers implfs fields in
596+
let projections = declare_projections rsp (projunivs,ubinders) ~kind:proj_kind inhabitant_id coers implfs fields in
596597
let build = GlobRef.ConstructRef cstr in
597598
let () = if is_coercion then ComCoercion.try_add_new_coercion build ~local:false ~poly in
598599
let struc = Structure.make (Global.env ()) rsp projections in
599600
let () = declare_structure_entry struc in
600-
rsp
601+
GlobRef.IndRef rsp
601602
in
602-
List.mapi map record_data
603+
List.mapi map record_data, []
603604

604605
let implicits_of_context ctx =
605606
List.map (fun name -> CAst.make (Some (name,true)))
606607
(List.rev (Anonymous :: (List.map RelDecl.get_name ctx)))
607608

608-
let build_class_constant ~univs ~rdata ~primitive_proj field implfs params paramimpls coers binder id proj_name =
609+
(* declare definitional class (typeclasses that are not record) *)
610+
let declare_class_constant ~univs paramimpls params data =
611+
let id, rdata, coers, inhabitant_id = match data with
612+
| [{ Data.id; rdata; is_coercion; coers; inhabitant_id }] ->
613+
assert (not is_coercion); (* should be ensured by caller *)
614+
let coers = List.map (fun { pf_subclass; pf_priority } -> if pf_subclass then Some { hint_priority = pf_priority; hint_pattern = None } else None) coers in
615+
id, rdata, coers, inhabitant_id
616+
| _ -> assert false in (* should be ensured by caller *)
617+
let implfs = rdata.DataR.implfs in
618+
let field, binder, proj_name = match rdata.DataR.fields with
619+
| [ LocalAssum ({binder_name=Name proj_name} as binder, field)
620+
| LocalDef ({binder_name=Name proj_name} as binder, _, field) ] ->
621+
let binder = {binder with binder_name=Name inhabitant_id} in
622+
field, binder, proj_name
623+
| _ -> assert false in (* should be ensured by caller *)
609624
let class_body = it_mkLambda_or_LetIn field params in
610625
let class_type = it_mkProd_or_LetIn rdata.DataR.arity params in
611626
let class_entry =
@@ -641,44 +656,29 @@ let build_class_constant ~univs ~rdata ~primitive_proj field implfs params param
641656
meth_info = sub;
642657
meth_const = Some proj_cst;
643658
} in
644-
[cref, [m]]
645-
646-
let build_record_constant ~rdata ~univs ~variances ~cumulative ~template ~primitive_proj
647-
fields params paramimpls is_coercion coers id idbuild inhabitant_id =
648-
let record_data =
649-
{ Data.id
650-
; idbuild
651-
; is_coercion = false
652-
(* to be replaced by the following line after deprecation phase
653-
(started in 8.16, c.f., https://github.com/coq/coq/pull/15802 ) *)
654-
(* ; is_coercion *)
655-
; coers = List.map (fun _ -> { pf_subclass = false ; pf_canonical = true }) coers
656-
(* to be replaced by the following line after deprecation phase
657-
(started in 8.16, c.f., https://github.com/coq/coq/pull/15802 ) *)
658-
(* ; coers = List.map (fun c -> { pf_subclass = c <> None ; pf_canonical = true }) coers *)
659-
; rdata
660-
; inhabitant_id
661-
} in
662-
let inds = declare_structure ~cumulative Declarations.BiFinite ~univs ~variances ~primitive_proj paramimpls
663-
params template ~kind:Decls.Method ~name:[|inhabitant_id|] [record_data]
664-
in
665-
let map ind =
666-
let map decl b y = {
667-
meth_name = RelDecl.get_name decl;
668-
meth_info = b;
669-
meth_const = y;
670-
} in
671-
let l = List.map3 map (List.rev fields) coers (Structure.find_projections ind) in
672-
GlobRef.IndRef ind, l
673-
in
674-
List.map map inds
659+
[cref], [m]
660+
661+
(* deprecated in 8.16, to be removed at the end of the deprecation phase
662+
(c.f., https://github.com/coq/coq/pull/15802 ) *)
663+
let warn_future_coercion_class_constructor =
664+
CWarnings.create ~name:"future-coercion-class-constructor" ~category:"records"
665+
~default:CWarnings.AsError
666+
Pp.(fun () -> str "'Class >' currently does nothing. Use 'Class' instead.")
667+
668+
(* deprecated in 8.16, to be removed at the end of the deprecation phase
669+
(c.f., https://github.com/coq/coq/pull/15802 ) *)
670+
let warn_future_coercion_class_field =
671+
CWarnings.create ~name:"future-coercion-class-field" ~category:"records" Pp.(fun () ->
672+
str "A coercion will be introduced in future versions when using ':>' in 'Class' declarations. "
673+
++ str "Use '#[global] Existing Instance field.' instead if you don't want the coercion.")
675674

676675
(** [declare_class] will prepare and declare a [Class]. This is done in
677676
2 steps:
678677
679-
1. two markedly different paths are followed depending on whether the
680-
class declaration refers to a constant "definitional classes" or to
681-
a record, that is to say:
678+
1. two markedly different paths were followed depending on whether the
679+
class declaration refers to a constant "definitional classes"
680+
(with [declare_class_constant]) or to a record (with [declare_structure]),
681+
that is to say:
682682
683683
Class foo := bar : T.
684684
@@ -692,29 +692,34 @@ let build_record_constant ~rdata ~univs ~variances ~cumulative ~template ~primit
692692
693693
Class foo := { ... }.
694694
695-
2. declare the class, using the information from 1. in the form of [Classes.typeclass]
695+
2. now, declare the class, using the information ([inds] and [def]) from 1.
696+
in the form of [Classes.typeclass]
696697
697698
*)
698-
let declare_class def ~cumulative ~univs ~variances ~primitive_proj id idbuild inhabitant_id paramimpls params
699-
rdata template ?(kind=Decls.StructureComponent) is_coercion coers =
700-
let implfs =
701-
(* Make the class implicit in the projections, and the params if applicable. *)
702-
let impls = implicits_of_context params in
703-
List.map (fun x -> impls @ x) rdata.DataR.implfs
704-
in
705-
let rdata = { rdata with DataR.implfs } in
706-
let fields = rdata.DataR.fields in
707-
let data =
708-
match fields with
709-
| [ LocalAssum ({binder_name=Name proj_name} as binder, field)
710-
| LocalDef ({binder_name=Name proj_name} as binder, _, field) ] when def ->
711-
assert (not is_coercion); (* should be ensured by caller *)
712-
let binder = {binder with binder_name=Name inhabitant_id} in
713-
build_class_constant ~rdata ~univs ~primitive_proj field implfs params paramimpls coers binder id proj_name
699+
let declare_class ~univs params inds def data =
700+
let id, idbuild, rdata, is_coercion, coers, inhabitant_id = match data with
701+
| [{ Data.id; idbuild; rdata; is_coercion; coers; inhabitant_id }] ->
702+
if is_coercion then warn_future_coercion_class_constructor ();
703+
if List.exists (fun pf -> pf.pf_subclass) coers then
704+
warn_future_coercion_class_field ();
705+
let coers = List.map (fun { pf_subclass; pf_priority } -> if pf_subclass then Some { hint_priority = pf_priority; hint_pattern = None } else None) coers in
706+
id, idbuild, rdata, is_coercion, coers, inhabitant_id
714707
| _ ->
715-
build_record_constant ~rdata ~univs ~variances ~cumulative ~template ~primitive_proj
716-
fields params paramimpls is_coercion coers id idbuild inhabitant_id
708+
CErrors.user_err (str "Mutual definitional classes are not handled.") in
709+
let fields = rdata.DataR.fields in
710+
let map ind =
711+
let map decl b y = {
712+
meth_name = RelDecl.get_name decl;
713+
meth_info = b;
714+
meth_const = y;
715+
} in
716+
let l = match ind with
717+
| GlobRef.IndRef ind ->
718+
List.map3 map (List.rev fields) coers (Structure.find_projections ind)
719+
| _ -> def in
720+
ind, l
717721
in
722+
let data = List.map map inds in
718723
let univs, params, fields =
719724
match fst univs with
720725
| UState.Polymorphic_entry uctx ->
@@ -739,9 +744,9 @@ let declare_class def ~cumulative ~univs ~variances ~primitive_proj id idbuild i
739744
in
740745
let env = Global.env () in
741746
let sigma = Evd.from_env env in
742-
Classes.add_class env sigma k; impl
747+
Classes.add_class env sigma k
743748
in
744-
List.map map data
749+
List.iter map data
745750

746751
let add_constant_class env sigma cst =
747752
let ty, univs = Typeops.type_of_global_in_context env (GlobRef.ConstRef cst) in
@@ -855,69 +860,6 @@ let extract_record_data records =
855860
in
856861
ps, data
857862

858-
(* deprecated in 8.16, to be removed at the end of the deprecation phase
859-
(c.f., https://github.com/coq/coq/pull/15802 ) *)
860-
let warn_future_coercion_class_constructor =
861-
CWarnings.create ~name:"future-coercion-class-constructor" ~category:"records"
862-
~default:CWarnings.AsError
863-
Pp.(fun () -> str "'Class >' currently does nothing. Use 'Class' instead.")
864-
865-
(* deprecated in 8.16, to be removed at the end of the deprecation phase
866-
(c.f., https://github.com/coq/coq/pull/15802 ) *)
867-
let warn_future_coercion_class_field =
868-
CWarnings.create ~name:"future-coercion-class-field" ~category:"records" Pp.(fun () ->
869-
str "A coercion will be introduced in future versions when using ':>' in 'Class' declarations. "
870-
++ str "Use '#[global] Existing Instance field.' instead if you don't want the coercion.")
871-
872-
(* declaring structures, common data to refactor *)
873-
let class_structure ~cumulative ~template ~impargs ~univs ~params ~primitive_proj def records data =
874-
let { Ast.name; is_coercion; cfs; idbuild; default_inhabitant_id; _ }, rdata = match records, data with
875-
| [r], [d] -> r, d
876-
| _, _ ->
877-
CErrors.user_err (str "Mutual definitional classes are not handled.")
878-
in
879-
if is_coercion then warn_future_coercion_class_constructor ();
880-
if List.exists (function (_, { rf_subclass = Vernacexpr.BackInstance; _ }) -> true | _ -> false) cfs then
881-
warn_future_coercion_class_field ();
882-
let coers = List.map (fun (_, { rf_subclass; rf_priority }) ->
883-
match rf_subclass with
884-
| Vernacexpr.BackInstance -> Some {hint_priority = rf_priority; hint_pattern = None}
885-
| Vernacexpr.NoInstance -> None)
886-
cfs
887-
in
888-
let inhabitant_id =
889-
match default_inhabitant_id with
890-
| None -> Namegen.next_ident_away name.CAst.v (Termops.vars_of_env (Global.env()))
891-
| Some id -> id
892-
in
893-
declare_class def ~cumulative ~univs ~primitive_proj name.CAst.v idbuild inhabitant_id
894-
impargs params rdata template is_coercion coers
895-
896-
let regular_structure ~cumulative ~template ~impargs ~univs ~variances ~params ~finite ~primitive_proj
897-
records data =
898-
let adjust_impls impls = impargs @ [CAst.make None] @ impls in
899-
let data = List.map (fun ({ DataR.implfs; _ } as d) -> { d with DataR.implfs = List.map adjust_impls implfs }) data in
900-
(* let map (min_univ, arity, fieldimpls, fields) { Ast.name; is_coercion; cfs; idbuild; _ } = *)
901-
let map rdata { Ast.name; is_coercion; cfs; idbuild; default_inhabitant_id; _ } =
902-
let coers = List.map (fun (_, { rf_subclass ; rf_canonical }) ->
903-
{ pf_subclass =
904-
(match rf_subclass with Vernacexpr.BackInstance -> true | Vernacexpr.NoInstance -> false);
905-
pf_canonical = rf_canonical })
906-
cfs
907-
in
908-
let inhabitant_id =
909-
match default_inhabitant_id with
910-
| None -> data_name name.CAst.v rdata
911-
| Some n -> n
912-
in
913-
{ Data.id = name.CAst.v; idbuild; rdata; is_coercion; coers; inhabitant_id }
914-
in
915-
let data = List.map2 map data records in
916-
let inds = declare_structure ~cumulative finite ~univs ~variances ~primitive_proj
917-
impargs params template data
918-
in
919-
List.map (fun ind -> GlobRef.IndRef ind) inds
920-
921863
(** [fs] corresponds to fields and [ps] to parameters; [coers] is a
922864
list telling if the corresponding fields must me declared as coercions
923865
or subinstances. *)
@@ -936,17 +878,50 @@ let definition_structure udecl kind ~template ~cumulative ~poly ~primitive_proj
936878
typecheck_params_and_fields (kind = Class true) poly udecl ps data) ()
937879
in
938880
let template = template, auto_template in
939-
match kind with
940-
| Class def ->
941-
class_structure ~template ~impargs ~cumulative ~params ~univs ~variances ~primitive_proj
942-
def records data
943-
| Inductive_kw | CoInductive | Variant | Record | Structure ->
944-
regular_structure ~cumulative ~template ~impargs ~univs ~variances ~params ~finite ~primitive_proj
945-
records data
881+
let is_class = match kind with Class def -> Some def
882+
| Inductive_kw | CoInductive | Variant | Record | Structure -> None in
883+
let adjust_impls impls = match is_class with
884+
| None -> impargs @ [CAst.make None] @ impls
885+
| Some _ -> implicits_of_context params @ impls in
886+
let data = List.map DataR.(fun d -> { d with implfs = List.map adjust_impls d.implfs }) data in
887+
let map rdata { Ast.name; is_coercion; cfs; idbuild; default_inhabitant_id; _ } =
888+
let coers = List.map (fun (_, { rf_subclass; rf_priority; rf_canonical }) ->
889+
{ pf_subclass = Vernacexpr.(match rf_subclass with BackInstance -> true | NoInstance -> false)
890+
; pf_priority = rf_priority; pf_canonical = rf_canonical })
891+
cfs
892+
in
893+
let inhabitant_id =
894+
match default_inhabitant_id, is_class with
895+
| None, Some _ -> Namegen.next_ident_away name.CAst.v (Termops.vars_of_env (Global.env()))
896+
| None, None -> data_name name.CAst.v rdata
897+
| Some id, _ -> id
898+
in
899+
{ Data.id = name.CAst.v; idbuild; rdata; is_coercion; coers; inhabitant_id }
900+
in
901+
let data = List.map2 map data records in
902+
let inds, def = match is_class with
903+
| Some true -> declare_class_constant ~univs impargs params data
904+
| Some false | None ->
905+
let proj_kind =
906+
Decls.(match is_class with None -> StructureComponent | _ -> Method) in
907+
(* remove the following block after deprecation phase
908+
(started in 8.16, c.f., https://github.com/coq/coq/pull/15802 ) *)
909+
let data' = if is_class = None then data else
910+
List.map (fun d ->
911+
{ d with
912+
Data.is_coercion = false
913+
; coers = List.map (fun _ -> { pf_subclass = false ; pf_priority = None ; pf_canonical = true }) d.Data.coers
914+
}) data in
915+
declare_structure ~cumulative finite ~univs ~variances ~primitive_proj
916+
~proj_kind impargs params template data'
917+
in
918+
if is_class <> None then declare_class ~univs params inds def data;
919+
inds
946920

947921
module Internal = struct
948922
type nonrec projection_flags = projection_flags = {
949923
pf_subclass: bool;
924+
pf_priority: int option;
950925
pf_canonical: bool;
951926
}
952927
let declare_projections = declare_projections

vernac/record.mli

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,14 @@ val declare_existing_class : GlobRef.t -> unit
4343
module Internal : sig
4444
type projection_flags = {
4545
pf_subclass: bool;
46+
pf_priority: int option;
4647
pf_canonical: bool;
4748
}
4849

4950
val declare_projections
5051
: Names.inductive
5152
-> Entries.universes_entry * UnivNames.universe_binders
52-
-> ?kind:Decls.definition_object_kind
53+
-> kind:Decls.definition_object_kind
5354
-> Names.Id.t
5455
-> projection_flags list
5556
-> Impargs.manual_implicits list

0 commit comments

Comments
 (0)