src/HOL/Tools/Lifting/lifting_def.ML
author kuncar
Tue May 29 19:13:02 2012 +0200 (2012-05-29)
changeset 48024 7599b28b707f
parent 47982 7aa35601ff65
child 49625 06cf80661e7a
permissions -rw-r--r--
don't be so aggressive during unfolding id and o
     1 (*  Title:      HOL/Tools/Lifting/lifting_def.ML
     2     Author:     Ondrej Kuncar
     3 
     4 Definitions for constants on quotient types.
     5 *)
     6 
     7 signature LIFTING_DEF =
     8 sig
     9   val add_lift_def:
    10     (binding * mixfix) -> typ -> term -> thm -> local_theory -> local_theory
    11 
    12   val lift_def_cmd:
    13     (binding * string option * mixfix) * string -> local_theory -> Proof.state
    14 
    15   val can_generate_code_cert: thm -> bool
    16 end;
    17 
    18 structure Lifting_Def: LIFTING_DEF =
    19 struct
    20 
    21 open Lifting_Util
    22 
    23 infix 0 MRSL
    24 
    25 (* Generation of the code certificate from the rsp theorem *)
    26 
    27 fun get_body_types (Type ("fun", [_, U]), Type ("fun", [_, V])) = get_body_types (U, V)
    28   | get_body_types (U, V)  = (U, V)
    29 
    30 fun get_binder_types (Type ("fun", [T, U]), Type ("fun", [V, W])) = (T, V) :: get_binder_types (U, W)
    31   | get_binder_types _ = []
    32 
    33 fun get_binder_types_by_rel (Const (@{const_name "fun_rel"}, _) $ _ $ S) (Type ("fun", [T, U]), Type ("fun", [V, W])) = 
    34     (T, V) :: get_binder_types_by_rel S (U, W)
    35   | get_binder_types_by_rel _ _ = []
    36 
    37 fun get_body_type_by_rel (Const (@{const_name "fun_rel"}, _) $ _ $ S) (Type ("fun", [_, U]), Type ("fun", [_, V])) = 
    38     get_body_type_by_rel S (U, V)
    39   | get_body_type_by_rel _ (U, V)  = (U, V)
    40 
    41 fun force_rty_type ctxt rty rhs = 
    42   let
    43     val thy = Proof_Context.theory_of ctxt
    44     val rhs_schematic = singleton (Variable.polymorphic ctxt) rhs
    45     val rty_schematic = fastype_of rhs_schematic
    46     val match = Sign.typ_match thy (rty_schematic, rty) Vartab.empty
    47   in
    48     Envir.subst_term_types match rhs_schematic
    49   end
    50 
    51 fun unabs_def ctxt def = 
    52   let
    53     val (_, rhs) = Thm.dest_equals (cprop_of def)
    54     fun dest_abs (Abs (var_name, T, _)) = (var_name, T)
    55       | dest_abs tm = raise TERM("get_abs_var",[tm])
    56     val (var_name, T) = dest_abs (term_of rhs)
    57     val (new_var_names, ctxt') = Variable.variant_fixes [var_name] ctxt
    58     val thy = Proof_Context.theory_of ctxt'
    59     val refl_thm = Thm.reflexive (cterm_of thy (Free (hd new_var_names, T)))
    60   in
    61     Thm.combination def refl_thm |>
    62     singleton (Proof_Context.export ctxt' ctxt)
    63   end
    64 
    65 fun unabs_all_def ctxt def = 
    66   let
    67     val (_, rhs) = Thm.dest_equals (cprop_of def)
    68     val xs = strip_abs_vars (term_of rhs)
    69   in  
    70     fold (K (unabs_def ctxt)) xs def
    71   end
    72 
    73 val map_fun_unfolded = 
    74   @{thm map_fun_def[abs_def]} |>
    75   unabs_def @{context} |>
    76   unabs_def @{context} |>
    77   Local_Defs.unfold @{context} [@{thm comp_def}]
    78 
    79 fun unfold_fun_maps ctm =
    80   let
    81     fun unfold_conv ctm =
    82       case (Thm.term_of ctm) of
    83         Const (@{const_name "map_fun"}, _) $ _ $ _ => 
    84           (Conv.arg_conv unfold_conv then_conv Conv.rewr_conv map_fun_unfolded) ctm
    85         | _ => Conv.all_conv ctm
    86   in
    87     (Conv.fun_conv unfold_conv) ctm
    88   end
    89 
    90 fun unfold_fun_maps_beta ctm =
    91   let val try_beta_conv = Conv.try_conv (Thm.beta_conversion false)
    92   in 
    93     (unfold_fun_maps then_conv try_beta_conv) ctm 
    94   end
    95 
    96 fun prove_rel ctxt rsp_thm (rty, qty) =
    97   let
    98     val ty_args = get_binder_types (rty, qty)
    99     fun disch_arg args_ty thm = 
   100       let
   101         val quot_thm = Lifting_Term.prove_quot_thm ctxt args_ty
   102       in
   103         [quot_thm, thm] MRSL @{thm apply_rsp''}
   104       end
   105   in
   106     fold disch_arg ty_args rsp_thm
   107   end
   108 
   109 exception CODE_CERT_GEN of string
   110 
   111 fun simplify_code_eq ctxt def_thm = 
   112   Local_Defs.unfold ctxt [@{thm o_apply}, @{thm map_fun_def}, @{thm id_apply}] def_thm
   113 
   114 (*
   115   quot_thm - quotient theorem (Quotient R Abs Rep T).
   116   returns: whether the Lifting package is capable to generate code for the abstract type
   117     represented by quot_thm
   118 *)
   119 
   120 fun can_generate_code_cert quot_thm  =
   121   case quot_thm_rel quot_thm of
   122     Const (@{const_name HOL.eq}, _) => true
   123     | Const (@{const_name invariant}, _) $ _  => true
   124     | _ => false
   125 
   126 fun generate_code_cert ctxt def_thm rsp_thm (rty, qty) =
   127   let
   128     val thy = Proof_Context.theory_of ctxt
   129     val quot_thm = Lifting_Term.prove_quot_thm ctxt (get_body_types (rty, qty))
   130     val fun_rel = prove_rel ctxt rsp_thm (rty, qty)
   131     val abs_rep_thm = [quot_thm, fun_rel] MRSL @{thm Quotient_rep_abs}
   132     val abs_rep_eq = 
   133       case (HOLogic.dest_Trueprop o prop_of) fun_rel of
   134         Const (@{const_name HOL.eq}, _) $ _ $ _ => abs_rep_thm
   135         | Const (@{const_name invariant}, _) $ _ $ _ $ _ => abs_rep_thm RS @{thm invariant_to_eq}
   136         | _ => raise CODE_CERT_GEN "relation is neither equality nor invariant"
   137     val unfolded_def = Conv.fconv_rule (Conv.arg_conv unfold_fun_maps_beta) def_thm
   138     val unabs_def = unabs_all_def ctxt unfolded_def
   139     val rep = (cterm_of thy o quot_thm_rep) quot_thm
   140     val rep_refl = Thm.reflexive rep RS @{thm meta_eq_to_obj_eq}
   141     val repped_eq = [rep_refl, unabs_def RS @{thm meta_eq_to_obj_eq}] MRSL @{thm cong}
   142     val code_cert = [repped_eq, abs_rep_eq] MRSL @{thm trans}
   143   in
   144     simplify_code_eq ctxt code_cert
   145   end
   146 
   147 fun generate_trivial_rep_eq ctxt def_thm =
   148   let
   149     val unfolded_def = Conv.fconv_rule (Conv.arg_conv unfold_fun_maps_beta) def_thm
   150     val code_eq = unabs_all_def ctxt unfolded_def
   151     val simp_code_eq = simplify_code_eq ctxt code_eq
   152   in
   153     simp_code_eq
   154   end
   155 
   156 fun generate_rep_eq ctxt def_thm rsp_thm (rty, qty) =
   157   if body_type rty = body_type qty then 
   158     SOME (generate_trivial_rep_eq ctxt def_thm)
   159   else 
   160     let
   161       val (rty_body, qty_body) = get_body_types (rty, qty)
   162       val quot_thm = Lifting_Term.prove_quot_thm ctxt (rty_body, qty_body)
   163     in
   164       if can_generate_code_cert quot_thm then
   165         SOME (generate_code_cert ctxt def_thm rsp_thm (rty, qty))
   166       else 
   167         NONE
   168     end
   169 
   170 fun generate_abs_eq ctxt def_thm rsp_thm quot_thm =
   171   let
   172     fun refl_tac ctxt =
   173       let
   174         fun intro_reflp_tac (t, i) = 
   175         let
   176           val concl_pat = Drule.strip_imp_concl (cprop_of @{thm reflpD})
   177           val insts = Thm.first_order_match (concl_pat, t)
   178         in
   179           rtac (Drule.instantiate_normalize insts @{thm reflpD}) i
   180         end
   181         handle Pattern.MATCH => no_tac
   182         
   183         val fun_rel_meta_eq = mk_meta_eq @{thm fun_rel_eq}
   184         val conv = Conv.bottom_conv (K (Conv.try_conv (Conv.rewr_conv fun_rel_meta_eq))) ctxt
   185         val rules = Lifting_Info.get_reflexivity_rules ctxt
   186       in
   187         EVERY' [CSUBGOAL intro_reflp_tac, 
   188                 CONVERSION conv,
   189                 REPEAT_ALL_NEW (resolve_tac rules)]
   190       end
   191     
   192     fun try_prove_prem ctxt prop =
   193       SOME (Goal.prove ctxt [] [] prop (fn {context, ...} => refl_tac context 1))
   194         handle ERROR _ => NONE
   195 
   196     val abs_eq_with_assms =
   197       let
   198         val (rty, qty) = quot_thm_rty_qty quot_thm
   199         val rel = quot_thm_rel quot_thm
   200         val ty_args = get_binder_types_by_rel rel (rty, qty)
   201         val body_type = get_body_type_by_rel rel (rty, qty)
   202         val quot_ret_thm = Lifting_Term.prove_quot_thm ctxt body_type
   203         
   204         val rep_abs_folded_unmapped_thm = 
   205           let
   206             val rep_id = [quot_thm, def_thm] MRSL @{thm Quotient_Rep_eq}
   207             val ctm = Thm.dest_equals_lhs (cprop_of rep_id)
   208             val unfolded_maps_eq = unfold_fun_maps ctm
   209             val t1 = [quot_thm, def_thm, rsp_thm] MRSL @{thm Quotient_rep_abs_fold_unmap}
   210             val prems_pat = (hd o Drule.cprems_of) t1
   211             val insts = Thm.first_order_match (prems_pat, cprop_of unfolded_maps_eq)
   212           in
   213             unfolded_maps_eq RS (Drule.instantiate_normalize insts t1)
   214           end
   215       in
   216         rep_abs_folded_unmapped_thm
   217         |> fold (fn _ => fn thm => thm RS @{thm fun_relD2}) ty_args
   218         |> (fn x => x RS (@{thm Quotient_rel_abs2} OF [quot_ret_thm]))
   219       end
   220     
   221     val prems = prems_of abs_eq_with_assms
   222     val indexed_prems = map_index (apfst (fn x => x + 1)) prems
   223     val indexed_assms = map (apsnd (try_prove_prem ctxt)) indexed_prems
   224     val proved_assms = map (apsnd the) (filter (is_some o snd) indexed_assms)
   225     val abs_eq = fold_rev (fn (i, assms) => fn thm => assms RSN (i, thm)) proved_assms abs_eq_with_assms
   226   in
   227     simplify_code_eq ctxt abs_eq
   228   end
   229 
   230 fun define_code_using_abs_eq abs_eq_thm lthy =
   231   if null (Logic.strip_imp_prems(prop_of abs_eq_thm)) then
   232     (snd oo Local_Theory.note) ((Binding.empty, [Code.add_default_eqn_attrib]), [abs_eq_thm]) lthy
   233   else
   234     lthy
   235   
   236 fun define_code_using_rep_eq maybe_rep_eq_thm lthy = 
   237   case maybe_rep_eq_thm of
   238     SOME rep_eq_thm =>   
   239       let
   240         val add_abs_eqn_attribute = 
   241           Thm.declaration_attribute (fn thm => Context.mapping (Code.add_abs_eqn thm) I)
   242         val add_abs_eqn_attrib = Attrib.internal (K add_abs_eqn_attribute);
   243       in
   244         (snd oo Local_Theory.note) ((Binding.empty, [add_abs_eqn_attrib]), [rep_eq_thm]) lthy
   245       end
   246     | NONE => lthy
   247 
   248 fun has_constr ctxt quot_thm =
   249   let
   250     val thy = Proof_Context.theory_of ctxt
   251     val abs_fun = quot_thm_abs quot_thm
   252   in
   253     if is_Const abs_fun then
   254       Code.is_constr thy ((fst o dest_Const) abs_fun)
   255     else
   256       false
   257   end
   258 
   259 fun has_abstr ctxt quot_thm =
   260   let
   261     val thy = Proof_Context.theory_of ctxt
   262     val abs_fun = quot_thm_abs quot_thm
   263   in
   264     if is_Const abs_fun then
   265       Code.is_abstr thy ((fst o dest_Const) abs_fun)
   266     else
   267       false
   268   end
   269 
   270 fun define_code abs_eq_thm maybe_rep_eq_thm (rty, qty) lthy =
   271   let
   272     val (rty_body, qty_body) = get_body_types (rty, qty)
   273   in
   274     if rty_body = qty_body then
   275       if null (Logic.strip_imp_prems(prop_of abs_eq_thm)) then
   276         (snd oo Local_Theory.note) ((Binding.empty, [Code.add_default_eqn_attrib]), [abs_eq_thm]) lthy
   277       else
   278         (snd oo Local_Theory.note) ((Binding.empty, [Code.add_default_eqn_attrib]), [the maybe_rep_eq_thm]) lthy
   279     else
   280       let 
   281         val body_quot_thm = Lifting_Term.prove_quot_thm lthy (rty_body, qty_body)
   282       in
   283         if has_constr lthy body_quot_thm then
   284           define_code_using_abs_eq abs_eq_thm lthy
   285         else if has_abstr lthy body_quot_thm then
   286           define_code_using_rep_eq maybe_rep_eq_thm lthy
   287         else
   288           lthy
   289       end
   290   end
   291 
   292 (*
   293   Defines an operation on an abstract type in terms of a corresponding operation 
   294     on a representation type.
   295 
   296   var - a binding and a mixfix of the new constant being defined
   297   qty - an abstract type of the new constant
   298   rhs - a term representing the new constant on the raw level
   299   rsp_thm - a respectfulness theorem in the internal tagged form (like '(R ===> R ===> R) f f'),
   300     i.e. "(Lifting_Term.equiv_relation (fastype_of rhs, qty)) $ rhs $ rhs"
   301 *)
   302 
   303 fun add_lift_def var qty rhs rsp_thm lthy =
   304   let
   305     val rty = fastype_of rhs
   306     val quot_thm = Lifting_Term.prove_quot_thm lthy (rty, qty)
   307     val absrep_trm =  quot_thm_abs quot_thm
   308     val rty_forced = (domain_type o fastype_of) absrep_trm
   309     val forced_rhs = force_rty_type lthy rty_forced rhs
   310     val lhs = Free (Binding.print (#1 var), qty)
   311     val prop = Logic.mk_equals (lhs, absrep_trm $ forced_rhs)
   312     val (_, prop') = Local_Defs.cert_def lthy prop
   313     val (_, newrhs) = Local_Defs.abs_def prop'
   314 
   315     val ((_, (_ , def_thm)), lthy') = 
   316       Local_Theory.define (var, ((Thm.def_binding (#1 var), []), newrhs)) lthy
   317 
   318     val transfer_thm = ([quot_thm, rsp_thm, def_thm] MRSL @{thm Quotient_to_transfer})
   319         |> Raw_Simplifier.rewrite_rule (Transfer.get_relator_eq lthy')
   320      
   321     val abs_eq_thm = generate_abs_eq lthy' def_thm rsp_thm quot_thm
   322     val maybe_rep_eq_thm = generate_rep_eq lthy' def_thm rsp_thm (rty_forced, qty)
   323 
   324     fun qualify defname suffix = Binding.qualified true suffix defname
   325 
   326     val lhs_name = (#1 var)
   327     val rsp_thm_name = qualify lhs_name "rsp"
   328     val abs_eq_thm_name = qualify lhs_name "abs_eq"
   329     val rep_eq_thm_name = qualify lhs_name "rep_eq"
   330     val transfer_thm_name = qualify lhs_name "transfer"
   331     val transfer_attr = Attrib.internal (K Transfer.transfer_add)
   332   in
   333     lthy'
   334       |> (snd oo Local_Theory.note) ((rsp_thm_name, []), [rsp_thm])
   335       |> (snd oo Local_Theory.note) ((transfer_thm_name, [transfer_attr]), [transfer_thm])
   336       |> (snd oo Local_Theory.note) ((abs_eq_thm_name, []), [abs_eq_thm])
   337       |> (case maybe_rep_eq_thm of 
   338             SOME rep_eq_thm => (snd oo Local_Theory.note) ((rep_eq_thm_name, []), [rep_eq_thm])
   339             | NONE => I)
   340       |> define_code abs_eq_thm maybe_rep_eq_thm (rty_forced, qty)
   341   end
   342 
   343 fun mk_readable_rsp_thm_eq tm lthy =
   344   let
   345     val ctm = cterm_of (Proof_Context.theory_of lthy) tm
   346     
   347     fun norm_fun_eq ctm = 
   348       let
   349         fun abs_conv2 cv = Conv.abs_conv (K (Conv.abs_conv (K cv) lthy)) lthy
   350         fun erase_quants ctm' =
   351           case (Thm.term_of ctm') of
   352             Const ("HOL.eq", _) $ _ $ _ => Conv.all_conv ctm'
   353             | _ => (Conv.binder_conv (K erase_quants) lthy then_conv 
   354               Conv.rewr_conv @{thm fun_eq_iff[symmetric, THEN eq_reflection]}) ctm'
   355       in
   356         (abs_conv2 erase_quants then_conv Thm.eta_conversion) ctm
   357       end
   358 
   359     fun simp_arrows_conv ctm =
   360       let
   361         val unfold_conv = Conv.rewrs_conv 
   362           [@{thm fun_rel_eq_invariant[THEN eq_reflection]}, @{thm fun_rel_eq_rel[THEN eq_reflection]}, 
   363             @{thm fun_rel_def[THEN eq_reflection]}]
   364         val left_conv = simp_arrows_conv then_conv Conv.try_conv norm_fun_eq
   365         fun binop_conv2 cv1 cv2 = Conv.combination_conv (Conv.arg_conv cv1) cv2
   366         val invariant_commute_conv = Conv.bottom_conv
   367           (K (Conv.try_conv (Conv.rewrs_conv (Lifting_Info.get_invariant_commute_rules lthy)))) lthy
   368       in
   369         case (Thm.term_of ctm) of
   370           Const (@{const_name "fun_rel"}, _) $ _ $ _ => 
   371             (binop_conv2  left_conv simp_arrows_conv then_conv unfold_conv) ctm
   372           | _ => invariant_commute_conv ctm
   373       end
   374     
   375     val unfold_ret_val_invs = Conv.bottom_conv 
   376       (K (Conv.try_conv (Conv.rewr_conv @{thm invariant_same_args}))) lthy 
   377     val simp_conv = Trueprop_conv (Conv.fun2_conv simp_arrows_conv)
   378     val univq_conv = Conv.rewr_conv @{thm HOL.all_simps(6)[symmetric, THEN eq_reflection]}
   379     val univq_prenex_conv = Conv.top_conv (K (Conv.try_conv univq_conv)) lthy
   380     val beta_conv = Thm.beta_conversion true
   381     val eq_thm = 
   382       (simp_conv then_conv univq_prenex_conv then_conv beta_conv then_conv unfold_ret_val_invs) ctm
   383   in
   384     Object_Logic.rulify(eq_thm RS Drule.equal_elim_rule2)
   385   end
   386 
   387 fun rename_to_tnames ctxt term =
   388   let
   389     fun all_typs (Const ("all", _) $ Abs (_, T, t)) = T :: all_typs t
   390       | all_typs _ = []
   391 
   392     fun rename (Const ("all", T1) $ Abs (_, T2, t)) (new_name :: names) = 
   393         (Const ("all", T1) $ Abs (new_name, T2, rename t names)) 
   394       | rename t _ = t
   395 
   396     val (fixed_def_t, _) = yield_singleton (Variable.importT_terms) term ctxt
   397     val new_names = Datatype_Prop.make_tnames (all_typs fixed_def_t)
   398   in
   399     rename term new_names
   400   end
   401 
   402 (*
   403 
   404   lifting_definition command. It opens a proof of a corresponding respectfulness 
   405   theorem in a user-friendly, readable form. Then add_lift_def is called internally.
   406 
   407 *)
   408 
   409 fun lift_def_cmd (raw_var, rhs_raw) lthy =
   410   let
   411     val ((binding, SOME qty, mx), lthy') = yield_singleton Proof_Context.read_vars raw_var lthy 
   412     val rhs = (Syntax.check_term lthy' o Syntax.parse_term lthy') rhs_raw
   413  
   414     fun try_to_prove_refl thm = 
   415       let
   416         val lhs_eq =
   417           thm
   418           |> prop_of
   419           |> Logic.dest_implies
   420           |> fst
   421           |> strip_all_body
   422           |> try HOLogic.dest_Trueprop
   423       in
   424         case lhs_eq of
   425           SOME (Const ("HOL.eq", _) $ _ $ _) => SOME (@{thm refl} RS thm)
   426           | _ => NONE
   427       end
   428 
   429     val rsp_rel = Lifting_Term.equiv_relation lthy' (fastype_of rhs, qty)
   430     val rty_forced = (domain_type o fastype_of) rsp_rel;
   431     val forced_rhs = force_rty_type lthy' rty_forced rhs;
   432     val internal_rsp_tm = HOLogic.mk_Trueprop (rsp_rel $ forced_rhs $ forced_rhs)
   433     val readable_rsp_thm_eq = mk_readable_rsp_thm_eq internal_rsp_tm lthy'
   434     val maybe_proven_rsp_thm = try_to_prove_refl readable_rsp_thm_eq
   435     val (readable_rsp_tm, _) = Logic.dest_implies (prop_of readable_rsp_thm_eq)
   436     val readable_rsp_tm_tnames = rename_to_tnames lthy' readable_rsp_tm
   437 
   438     fun after_qed thm_list lthy = 
   439       let
   440         val internal_rsp_thm =
   441           case thm_list of
   442             [] => the maybe_proven_rsp_thm
   443           | [[thm]] => Goal.prove lthy [] [] internal_rsp_tm 
   444             (fn _ => rtac readable_rsp_thm_eq 1 THEN Proof_Context.fact_tac [thm] 1)
   445       in
   446         add_lift_def (binding, mx) qty rhs internal_rsp_thm lthy
   447       end
   448 
   449   in
   450     case maybe_proven_rsp_thm of
   451       SOME _ => Proof.theorem NONE after_qed [] lthy'
   452       | NONE =>  Proof.theorem NONE after_qed [[(readable_rsp_tm_tnames,[])]] lthy'
   453   end
   454 
   455 fun quot_thm_err ctxt (rty, qty) pretty_msg =
   456   let
   457     val error_msg = cat_lines
   458        ["Lifting failed for the following types:",
   459         Pretty.string_of (Pretty.block
   460          [Pretty.str "Raw type:", Pretty.brk 2, Syntax.pretty_typ ctxt rty]),
   461         Pretty.string_of (Pretty.block
   462          [Pretty.str "Abstract type:", Pretty.brk 2, Syntax.pretty_typ ctxt qty]),
   463         "",
   464         (Pretty.string_of (Pretty.block
   465          [Pretty.str "Reason:", Pretty.brk 2, pretty_msg]))]
   466   in
   467     error error_msg
   468   end
   469 
   470 fun check_rty_err ctxt (rty_schematic, rty_forced) (raw_var, rhs_raw) =
   471   let
   472     val (_, ctxt') = yield_singleton Proof_Context.read_vars raw_var ctxt 
   473     val rhs = (Syntax.check_term ctxt' o Syntax.parse_term ctxt') rhs_raw
   474     val error_msg = cat_lines
   475        ["Lifting failed for the following term:",
   476         Pretty.string_of (Pretty.block
   477          [Pretty.str "Term:", Pretty.brk 2, Syntax.pretty_term ctxt rhs]),
   478         Pretty.string_of (Pretty.block
   479          [Pretty.str "Type:", Pretty.brk 2, Syntax.pretty_typ ctxt rty_schematic]),
   480         "",
   481         (Pretty.string_of (Pretty.block
   482          [Pretty.str "Reason:", 
   483           Pretty.brk 2, 
   484           Pretty.str "The type of the term cannot be instancied to",
   485           Pretty.brk 1,
   486           Pretty.quote (Syntax.pretty_typ ctxt rty_forced),
   487           Pretty.str "."]))]
   488     in
   489       error error_msg
   490     end
   491 
   492 fun lift_def_cmd_with_err_handling (raw_var, rhs_raw) lthy =
   493   (lift_def_cmd (raw_var, rhs_raw) lthy
   494     handle Lifting_Term.QUOT_THM (rty, qty, msg) => quot_thm_err lthy (rty, qty) msg)
   495     handle Lifting_Term.CHECK_RTY (rty_schematic, rty_forced) => 
   496       check_rty_err lthy (rty_schematic, rty_forced) (raw_var, rhs_raw)
   497 
   498 (* parser and command *)
   499 val liftdef_parser =
   500   ((Parse.binding -- (@{keyword "::"} |-- (Parse.typ >> SOME) -- Parse.opt_mixfix')) >> Parse.triple2)
   501     --| @{keyword "is"} -- Parse.term
   502 
   503 val _ =
   504   Outer_Syntax.local_theory_to_proof @{command_spec "lift_definition"}
   505     "definition for constants over the quotient type"
   506       (liftdef_parser >> lift_def_cmd_with_err_handling)
   507 
   508 
   509 end; (* structure *)