Skip to content

Commit 0dc2518

Browse files
committed
Error instead of renaming section variable mentioned through a global
Fix #12304
1 parent 8939b7c commit 0dc2518

7 files changed

Lines changed: 92 additions & 21 deletions

File tree

engine/evarutil.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ type unification_pb = conv_pb * env * constr * constr
199199
Put it at the end of the list if [tail] is true, by default it is false. *)
200200
val add_unification_pb : ?tail:bool -> unification_pb -> evar_map -> evar_map
201201

202+
val vars_of_global : env -> evar_map -> GlobRef.t -> Id.Set.t
203+
202204
(** {6 Removing hyps in evars'context}
203205
raise OccurHypInSimpleClause if the removal breaks dependencies *)
204206

kernel/context.ml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,8 @@ struct
377377
let set_id id =
378378
let set x = {x with binder_name = id} in
379379
function
380-
| LocalAssum (s, x, ty) -> LocalAssum (s, set x, ty)
381-
| LocalDef (s, x, v, ty) -> LocalDef (s, set x, v, ty)
380+
| LocalAssum (_, x, ty) -> LocalAssum (ProofVar, set x, ty)
381+
| LocalDef (_, x, v, ty) -> LocalDef (ProofVar, set x, v, ty)
382382

383383
(** Return [true] iff a given declaration is a local assumption. *)
384384
let is_local_assum = function
@@ -414,6 +414,7 @@ struct
414414
let map_id f x =
415415
let id = get_id x in
416416
let id' = f id in
417+
(* XXX use Id.equal? *)
417418
if id == id' then x else set_id id' x
418419

419420
(** Map all terms in a given declaration. *)

kernel/context.mli

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ sig
248248

249249
val get_relevance : ('c, 't, 'r) pt -> 'r
250250

251-
(** Set the identifier that is bound by a given declaration. *)
251+
(** Set the identifier that is bound by a given declaration. status becomes [ProofVar]. *)
252252
val set_id : Id.t -> ('c, 't, 'r) pt -> ('c, 't, 'r) pt
253253

254254
(** Return [true] iff a given declaration is a local assumption. *)
@@ -267,7 +267,8 @@ sig
267267
val equal : ('r -> 'r -> bool) -> ('c -> 'c -> bool) ->
268268
('c, 'c, 'r) pt -> ('c, 'c, 'r) pt -> bool
269269

270-
(** Map the identifier bound by a given declaration. *)
270+
(** Map the identifier bound by a given declaration.
271+
Status becomes [ProofVar] if the id changes (physical equality). *)
271272
val map_id : (Id.t -> Id.t) -> ('c, 't, 'r) pt -> ('c, 't, 'r) pt
272273

273274
(** Map all terms in a given declaration. *)

pretyping/evarsolve.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,8 @@ let free_vars_and_rels_up_alias_expansion env sigma aliases c =
571571
| Some (RelAlias n) -> if n >= depth+1 then fv_rels := Int.Set.add (n-depth) !fv_rels
572572
| None -> frec (aliases,depth) c end
573573
| Const _ | Ind _ | Construct _ ->
574-
fv_ids := Id.Set.union (vars_of_global env (fst @@ EConstr.destRef sigma c)) !fv_ids
574+
(* XXX should be Evarutil.vars_of_global to handle abstracted constants? *)
575+
fv_ids := Id.Set.union (Environ.vars_of_global env (fst @@ EConstr.destRef sigma c)) !fv_ids
575576
| _ ->
576577
iter_with_full_binders env sigma
577578
(fun d (aliases,depth) -> (extend_alias sigma d aliases,depth+1))

tactics/hints.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,7 @@ let with_uid c = { obj = c; uid = fresh_key () }
858858

859859
let secvars_of_idset s =
860860
Id.Set.fold (fun id p ->
861+
(* XXX how can vars_of_global return non secvars? *)
861862
if is_section_variable (Global.env ()) id then
862863
Id.Pred.add id p
863864
else p) s Id.Pred.empty

tactics/tactics.ml

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,74 @@ let move_hyp id dest =
217217
end
218218
end
219219

220+
let error_renaming_implicit_dependency ?loc env where ids gr =
221+
CErrors.user_err ?loc @@
222+
fmt "Cannot rename section variable %t@ because it is used implicitly through %t@ in %t."
223+
(fun () -> Id.print (Id.Set.choose ids))
224+
(fun () -> pr_global_env env gr)
225+
(fun () -> match where with
226+
| None -> str "the conclusion"
227+
| Some h -> fmt "hypothesis %t" (fun () -> Id.print h))
228+
229+
let check_renaming ~src ~dst env sigma concl =
230+
let sign = named_context_val env in
231+
(* Check that we do not mess variables *)
232+
let vars = ids_of_named_context_val sign in
233+
let () =
234+
if not (Id.Set.subset src vars) then
235+
let hyp = Id.Set.choose (Id.Set.diff src vars) in
236+
raise (RefinerError (env, sigma, NoSuchHyp hyp))
237+
in
238+
let mods = Id.Set.diff vars src in
239+
let () =
240+
try
241+
let elt = Id.Set.choose (Id.Set.inter dst mods) in
242+
TacticErrors.already_used elt
243+
with Not_found -> ()
244+
in
245+
let secvars =
246+
Id.Set.filter (fun id ->
247+
match NamedDecl.get_status (lookup_named id env) with
248+
| SecVar -> true
249+
| ProofVar -> false)
250+
src
251+
in
252+
let checked = ref GlobRef.Set_env.empty in
253+
let check_constr where c =
254+
let rec aux c =
255+
match EConstr.destRef sigma c with
256+
| VarRef _, _ ->
257+
(* we only refuse implicit dependencies, because they can't be substituted *)
258+
()
259+
| gr, _ ->
260+
if GlobRef.Set_env.mem gr !checked then ()
261+
else begin
262+
let deps = Evarutil.vars_of_global env sigma gr in
263+
let bad = Id.Set.inter deps secvars in
264+
let () =
265+
if not @@ Id.Set.is_empty bad then
266+
error_renaming_implicit_dependency env where bad gr
267+
in
268+
checked := GlobRef.Set_env.add gr !checked
269+
end
270+
| exception DestKO -> EConstr.iter sigma aux c
271+
in
272+
aux c
273+
in
274+
let () =
275+
if Id.Set.is_empty secvars then
276+
(* not renaming any secvars -> no problem *)
277+
()
278+
else
279+
let () = check_constr None concl in
280+
let () =
281+
List.iter (fun d -> NamedDecl.iter_constr (check_constr (Some (NamedDecl.get_id d))) d)
282+
(named_context env)
283+
in
284+
()
285+
in
286+
()
287+
220288
let rename_hyp repl =
221289
let fold accu (src, dst) = match accu with
222290
| None -> None
@@ -238,30 +306,16 @@ let rename_hyp repl =
238306
Proofview.Goal.enter begin fun gl ->
239307
let concl = Proofview.Goal.concl gl in
240308
let env = Proofview.Goal.env gl in
241-
let sign = named_context_val env in
242309
let sigma = Proofview.Goal.sigma gl in
243310
let relevance = Proofview.Goal.relevance gl in
244-
(* Check that we do not mess variables *)
245-
let vars = ids_of_named_context_val sign in
246-
let () =
247-
if not (Id.Set.subset src vars) then
248-
let hyp = Id.Set.choose (Id.Set.diff src vars) in
249-
raise (RefinerError (env, sigma, NoSuchHyp hyp))
250-
in
251-
let mods = Id.Set.diff vars src in
252-
let () =
253-
try
254-
let elt = Id.Set.choose (Id.Set.inter dst mods) in
255-
TacticErrors.already_used elt
256-
with Not_found -> ()
257-
in
311+
let () = check_renaming ~src ~dst env sigma concl in
258312
(* All is well *)
259313
let make_subst (src, dst) = (src, mkVar dst) in
260314
let subst = List.map make_subst repl in
261315
let subst c = Vars.replace_vars sigma subst c in
262316
let replace id = try List.assoc_f Id.equal id repl with Not_found -> id in
263317
let map decl = decl |> NamedDecl.map_id replace |> NamedDecl.map_constr subst in
264-
let ohyps = named_context_of_val sign in
318+
let ohyps = EConstr.named_context env in
265319
let nhyps = List.map map ohyps in
266320
let nconcl = subst concl in
267321
let nctx = val_of_named_context nhyps in

test-suite/bugs/bug_12304.v

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Section S.
2+
Variable a:nat.
3+
Definition b:=a.
4+
Goal b=b.
5+
Proof.
6+
Fail rename a into c.
7+
generalize b. intros b.
8+
rename a into c.
9+
Fail unfold b.
10+
Abort.
11+
End S.

0 commit comments

Comments
 (0)