1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
(************************************************************************) (* * The Coq Proof Assistant / The Coq Development Team *) (* v * INRIA, CNRS and contributors - Copyright 1999-2019 *) (* <O___,, * (see CREDITS file for the list of authors) *) (* \VV/ **************************************************************) (* // * This file is distributed under the terms of the *) (* * GNU Lesser General Public License Version 2.1 *) (* * (see LICENSE file for the text of the license) *) (************************************************************************) open CErrors open Util open Pp open Names open Term open Constr open Context open Vars open Termops open Environ open Classops open Declare open Libobject let strength_min l = if List.mem `LOCAL l then `LOCAL else `GLOBAL let loc_of_bool b = if b then `LOCAL else `GLOBAL (* Errors *) type coercion_error_kind = | AlreadyExists | NotAFunction | NoSource of cl_typ option | ForbiddenSourceClass of cl_typ | NoTarget | WrongTarget of cl_typ * cl_typ | NotAClass of GlobRef.t exception CoercionError of coercion_error_kind let explain_coercion_error g = function | AlreadyExists -> (Printer.pr_global g ++ str" is already a coercion") | NotAFunction -> (Printer.pr_global g ++ str" is not a function") | NoSource (Some cl) -> (str "Cannot recognize " ++ pr_class cl ++ str " as a source class of " ++ Printer.pr_global g) | NoSource None -> (str ": cannot find the source class of " ++ Printer.pr_global g) | ForbiddenSourceClass cl -> pr_class cl ++ str " cannot be a source class" | NoTarget -> (str"Cannot find the target class") | WrongTarget (clt,cl) -> (str"Found target class " ++ pr_class cl ++ str " instead of " ++ pr_class clt) | NotAClass ref -> (str "Type of " ++ Printer.pr_global ref ++ str " does not end with a sort") (* Verifications pour l'ajout d'une classe *) let check_reference_arity ref = let env = Global.env () in let c, _ = Typeops.type_of_global_in_context env ref in if not (Reductionops.is_arity env (Evd.from_env env) (EConstr.of_constr c)) (* FIXME *) then raise (CoercionError (NotAClass ref)) let check_arity = function | CL_FUN | CL_SORT -> () | CL_CONST cst -> check_reference_arity (GlobRef.ConstRef cst) | CL_PROJ p -> check_reference_arity (GlobRef.ConstRef (Projection.Repr.constant p)) | CL_SECVAR id -> check_reference_arity (GlobRef.VarRef id) | CL_IND kn -> check_reference_arity (GlobRef.IndRef kn) (* Coercions *) (* check that the computed target is the provided one *) let check_target clt = function | Some cl when not (cl_typ_eq cl clt) -> raise (CoercionError (WrongTarget(clt,cl))) | _ -> () (* condition d'heritage uniforme *) let uniform_cond sigma ctx lt = List.for_all2eq (EConstr.eq_constr sigma) lt (Context.Rel.to_extended_list EConstr.mkRel 0 ctx) let class_of_global = function | GlobRef.ConstRef sp -> (match Recordops.find_primitive_projection sp with | Some p -> CL_PROJ p | None -> CL_CONST sp) | GlobRef.IndRef sp -> CL_IND sp | GlobRef.VarRef id -> CL_SECVAR id | GlobRef.ConstructRef _ as c -> user_err ~hdr:"class_of_global" (str "Constructors, such as " ++ Printer.pr_global c ++ str ", cannot be used as a class.") (* lp est la liste (inverse'e) des arguments de la coercion ids est le nom de la classe source sps_opt est le sp de la classe source dans le cas des structures retourne: la classe source nbre d'arguments de la classe le constr de la class la liste des variables dont depend la classe source l'indice de la classe source dans la liste lp *) let get_source lp source = let open Context.Rel.Declaration in match source with | None -> (* Take the latest non let-in argument *) let rec aux = function | [] -> raise Not_found | LocalDef _ :: lt -> aux lt | LocalAssum (_,t1) :: lt -> let cl1,u1,lv1 = find_class_type Evd.empty (EConstr.of_constr t1) in cl1,lt,lv1,1 in aux lp | Some cl -> (* Take the first argument that matches *) let rec aux acc = function | [] -> raise Not_found | LocalDef _ as decl :: lt -> aux (decl::acc) lt | LocalAssum (_,t1) as decl :: lt -> try let cl1,u1,lv1 = find_class_type Evd.empty (EConstr.of_constr t1) in if cl_typ_eq cl cl1 then cl1,acc,lv1,Context.Rel.nhyps lt+1 else raise Not_found with Not_found -> aux (decl::acc) lt in aux [] (List.rev lp) let get_target t ind = if (ind > 1) then CL_FUN else match pi1 (find_class_type Evd.empty (EConstr.of_constr t)) with | CL_CONST p when Recordops.is_primitive_projection p -> CL_PROJ (Option.get @@ Recordops.find_primitive_projection p) | x -> x let strength_of_cl = function | CL_CONST kn -> `GLOBAL | CL_SECVAR id -> `LOCAL | _ -> `GLOBAL let strength_of_global = function | GlobRef.VarRef _ -> `LOCAL | _ -> `GLOBAL let get_strength stre ref cls clt = let stres = strength_of_cl cls in let stret = strength_of_cl clt in let stref = strength_of_global ref in strength_min [stre;stres;stret;stref] let ident_key_of_class = function | CL_FUN -> "Funclass" | CL_SORT -> "Sortclass" | CL_CONST sp -> Label.to_string (Constant.label sp) | CL_PROJ sp -> Label.to_string (Projection.Repr.label sp) | CL_IND (sp,_) -> Label.to_string (MutInd.label sp) | CL_SECVAR id -> Id.to_string id (* Identity coercion *) let error_not_transparent source = user_err ~hdr:"build_id_coercion" (pr_class source ++ str " must be a transparent constant.") let build_id_coercion idf_opt source poly = let env = Global.env () in let sigma = Evd.from_env env in let sigma, vs = match source with | CL_CONST sp -> Evd.fresh_global env sigma (GlobRef.ConstRef sp) | _ -> error_not_transparent source in let vs = EConstr.Unsafe.to_constr vs in let c = match constant_opt_value_in env (destConst vs) with | Some c -> c | None -> error_not_transparent source in let lams,t = decompose_lam_assum c in let val_f = it_mkLambda_or_LetIn (mkLambda (make_annot (Name Namegen.default_dependent_ident) Sorts.Relevant, applistc vs (Context.Rel.to_extended_list mkRel 0 lams), mkRel 1)) lams in let typ_f = List.fold_left (fun d c -> Term.mkProd_wo_LetIn c d) (mkProd (make_annot Anonymous Sorts.Relevant, applistc vs (Context.Rel.to_extended_list mkRel 0 lams), lift 1 t)) lams in (* juste pour verification *) let _ = if not (Reductionops.is_conv_leq env sigma (Typing.unsafe_type_of env sigma (EConstr.of_constr val_f)) (EConstr.of_constr typ_f)) then user_err (strbrk "Cannot be defined as coercion (maybe a bad number of arguments).") in let name = match idf_opt with | Some idf -> idf | None -> let cl,u,_ = find_class_type sigma (EConstr.of_constr t) in Id.of_string ("Id_"^(ident_key_of_class source)^"_"^ (ident_key_of_class cl)) in let univs = Evd.univ_entry ~poly sigma in let constr_entry = (* Cast is necessary to express [val_f] is identity *) DefinitionEntry (definition_entry ~types:typ_f ~univs ~inline:true (mkCast (val_f, DEFAULTcast, typ_f))) in let kind = Decls.(IsDefinition IdentityCoercion) in let kn = declare_constant ~name ~kind constr_entry in GlobRef.ConstRef kn let check_source = function | Some (CL_FUN as s) -> raise (CoercionError (ForbiddenSourceClass s)) | _ -> () let cache_coercion (_,c) = let env = Global.env () in let sigma = Evd.from_env env in Classops.declare_coercion env sigma c let open_coercion i o = if Int.equal i 1 then cache_coercion o let discharge_coercion (_, c) = if c.coercion_local then None else let n = try let ins = Lib.section_instance c.coercion_type in Array.length (snd ins) with Not_found -> 0 in let nc = { c with coercion_params = n + c.coercion_params; coercion_is_proj = Option.map Lib.discharge_proj_repr c.coercion_is_proj; } in Some nc let classify_coercion obj = if obj.coercion_local then Dispose else Substitute obj let inCoercion : coercion -> obj = declare_object {(default_object "COERCION") with open_function = open_coercion; cache_function = cache_coercion; subst_function = (fun (subst,c) -> subst_coercion subst c); classify_function = classify_coercion; discharge_function = discharge_coercion } let declare_coercion coef ?(local = false) ~isid ~src:cls ~target:clt ~params:ps = let isproj = match coef with | GlobRef.ConstRef c -> Recordops.find_primitive_projection c | _ -> None in let c = { coercion_type = coef; coercion_local = local; coercion_is_id = isid; coercion_is_proj = isproj; coercion_source = cls; coercion_target = clt; coercion_params = ps; } in Lib.add_anonymous_leaf (inCoercion c) (* nom de la fonction coercion strength de f nom de la classe source (optionnel) sp de la classe source (dans le cas des structures) nom de la classe target (optionnel) booleen "coercion identite'?" lorque source est None alors target est None aussi. *) let warn_uniform_inheritance = CWarnings.create ~name:"uniform-inheritance" ~category:"typechecker" (fun g -> Printer.pr_global g ++ strbrk" does not respect the uniform inheritance condition") let add_new_coercion_core coef stre poly source target isid = check_source source; let t, _ = Typeops.type_of_global_in_context (Global.env ()) coef in if coercion_exists coef then raise (CoercionError AlreadyExists); let lp,tg = decompose_prod_assum t in let llp = List.length lp in if Int.equal llp 0 then raise (CoercionError NotAFunction); let (cls,ctx,lvs,ind) = try get_source lp source with Not_found -> raise (CoercionError (NoSource source)) in check_source (Some cls); if not (uniform_cond Evd.empty (* FIXME - for when possibly called with unresolved evars in the future *) ctx lvs) then warn_uniform_inheritance coef; let clt = try get_target tg ind with Not_found -> raise (CoercionError NoTarget) in check_target clt target; check_arity cls; check_arity clt; let local = match get_strength stre coef cls clt with | `LOCAL -> true | `GLOBAL -> false in declare_coercion coef ~local ~isid ~src:cls ~target:clt ~params:(List.length lvs) let try_add_new_coercion_core ref ~local c d e f = try add_new_coercion_core ref (loc_of_bool local) c d e f with CoercionError e -> user_err ~hdr:"try_add_new_coercion_core" (explain_coercion_error ref e ++ str ".") let try_add_new_coercion ref ~local ~poly = try_add_new_coercion_core ref ~local poly None None false let try_add_new_coercion_subclass cl ~local ~poly = let coe_ref = build_id_coercion None cl poly in try_add_new_coercion_core coe_ref ~local poly (Some cl) None true let try_add_new_coercion_with_target ref ~local ~poly ~source ~target = try_add_new_coercion_core ref ~local poly (Some source) (Some target) false let try_add_new_identity_coercion id ~local ~poly ~source ~target = let ref = build_id_coercion (Some id) source poly in try_add_new_coercion_core ref ~local poly (Some source) (Some target) true let try_add_new_coercion_with_source ref ~local ~poly ~source = try_add_new_coercion_core ref ~local poly (Some source) None false let add_coercion_hook poly { DeclareDef.Hook.S.scope; dref; _ } = let open DeclareDef in let local = match scope with | Discharge -> assert false (* Local Coercion in section behaves like Local Definition *) | Global ImportNeedQualified -> true | Global ImportDefaultBehavior -> false in let () = try_add_new_coercion dref ~local ~poly in let msg = Nametab.pr_global_env Id.Set.empty dref ++ str " is now a coercion" in Flags.if_verbose Feedback.msg_info msg let add_coercion_hook ~poly = DeclareDef.Hook.make (add_coercion_hook poly) let add_subclass_hook ~poly { DeclareDef.Hook.S.scope; dref; _ } = let open DeclareDef in let stre = match scope with | Discharge -> assert false (* Local Subclass in section behaves like Local Definition *) | Global ImportNeedQualified -> true | Global ImportDefaultBehavior -> false in let cl = class_of_global dref in try_add_new_coercion_subclass cl ~local:stre ~poly let add_subclass_hook ~poly = DeclareDef.Hook.make (add_subclass_hook ~poly)