diff --git a/library/init/meta/interactive.lean b/library/init/meta/interactive.lean index d52e76cbf9..0ad942a5dd 100644 --- a/library/init/meta/interactive.lean +++ b/library/init/meta/interactive.lean @@ -312,7 +312,8 @@ do e ← i_to_expr p, match rec_name with | some n := induction_core semireducible e n ids | none := do I ← get_type_name e, induction_core semireducible e (I <.> "rec") ids - end + end, + return () meta def cases (p : qexpr0) (ids : with_ident_list) : tactic unit := do e ← i_to_expr p, diff --git a/library/init/meta/mk_dec_eq_instance.lean b/library/init/meta/mk_dec_eq_instance.lean index 2d3e0b7c1e..6bcc19753d 100644 --- a/library/init/meta/mk_dec_eq_instance.lean +++ b/library/init/meta/mk_dec_eq_instance.lean @@ -104,7 +104,7 @@ do I_name ← get_dec_eq_type_name, -- Use brec_on if type is recursive. -- We store the functional in the variable F. if is_recursive env I_name - then intro1 >>= (λ x, induction_core semireducible x (I_name <.> "brec_on") (idx_names ++ [v_name, F_name])) + then intro1 >>= (λ x, induction_core semireducible x (I_name <.> "brec_on") (idx_names ++ [v_name, F_name]) >> return ()) else intro v_name >> return (), -- Apply cases to first element of type (I ...) diff --git a/library/init/meta/mk_has_sizeof_instance.lean b/library/init/meta/mk_has_sizeof_instance.lean index d8cb6b26c6..1f4cb895c2 100644 --- a/library/init/meta/mk_has_sizeof_instance.lean +++ b/library/init/meta/mk_has_sizeof_instance.lean @@ -67,7 +67,7 @@ do I_name ← get_has_sizeof_type_name, -- Use brec_on if type is recursive. -- We store the functional in the variable F. if is_recursive env I_name - then intro `_v >>= (λ x, induction_core semireducible x (I_name <.> "brec_on") [v_name, F_name]) + then intro `_v >>= (λ x, induction_core semireducible x (I_name <.> "brec_on") [v_name, F_name] >> return ()) else intro v_name >> return (), arg_names : list (list name) ← mk_constructors_arg_names I_name `_p, get_local v_name >>= λ v, cases_using v (join arg_names), diff --git a/library/init/meta/smt/smt_tactic.lean b/library/init/meta/smt/smt_tactic.lean index 873a62ea32..e932897510 100644 --- a/library/init/meta/smt/smt_tactic.lean +++ b/library/init/meta/smt/smt_tactic.lean @@ -361,7 +361,7 @@ meta def add_lemmas_from_facts : smt_tactic unit := get_facts >>= add_lemmas_from_facts_core meta def induction (e : expr) (rec : name) (ids : list name) : smt_tactic unit := -slift (tactic.induction e rec ids) +slift (tactic.induction e rec ids >> return ()) -- pass on the information? end smt_tactic diff --git a/library/init/meta/tactic.lean b/library/init/meta/tactic.lean index c59a1c0ff5..c3195d5491 100644 --- a/library/init/meta/tactic.lean +++ b/library/init/meta/tactic.lean @@ -413,8 +413,13 @@ meta constant abstract_hash : expr → tactic nat meta constant abstract_weight : expr → tactic nat meta constant abstract_eq : expr → expr → tactic bool /- (induction_core m H rec ns) induction on H using recursor rec, names for the new hypotheses - are retrieved from ns. If ns does not have sufficient names, then use the internal binder names in the recursor. -/ -meta constant induction_core : transparency → expr → name → list name → tactic unit + are retrieved from ns. If ns does not have sufficient names, then use the internal binder names + in the recursor. + It returns for each new goal a list of new hypotheses and a list of substitutions for hypotheses + depending on H. The substitutions map internal names to their replacement terms. If the + replacement is again a hypothesis the user name stays the same. The internal names are only valid + in the original goal, not in the type context of the new goal. -/ +meta constant induction_core : transparency → expr → name → list name → tactic (list (list expr × list (name × expr))) /- (cases_core m H ns) apply cases_on recursor, names for the new hypotheses are retrieved from ns. H must be a local constant -/ meta constant cases_core : transparency → expr → list name → tactic unit @@ -866,7 +871,7 @@ cases_core semireducible H [] meta def cases_using : expr → list name → tactic unit := cases_core semireducible -meta def induction : expr → name → list name → tactic unit := +meta def induction : expr → name → list name → tactic (list (list expr × list (name × expr))) := induction_core semireducible meta def destruct (e : expr) : tactic unit := diff --git a/src/library/tactic/induction_tactic.cpp b/src/library/tactic/induction_tactic.cpp index 88a3520900..5a717fa2b5 100644 --- a/src/library/tactic/induction_tactic.cpp +++ b/src/library/tactic/induction_tactic.cpp @@ -320,10 +320,25 @@ vm_obj induction_tactic_core(transparency_mode const & m, expr const & H, name c try { metavar_context mctx = s.mctx(); list tmp_ns = ns; + list> hyps; + hsubstitution_list substs; list new_goals = induction(s.env(), s.get_options(), m, mctx, head(s.goals()), H, rec_name, tmp_ns, - nullptr, nullptr); + &hyps, &substs); tactic_state new_s = set_mctx_goals(s, mctx, append(new_goals, tail(s.goals()))); - return mk_tactic_success(new_s); + + buffer info; + while (!is_nil(hyps)) { + vm_obj hyps_obj = to_obj(head(hyps)); + buffer substs_objs; + head(substs).for_each([&](name const & from, expr const & to) { + substs_objs.push_back(mk_vm_pair(to_obj(from), to_obj(to))); + }); + + info.push_back(mk_vm_pair(hyps_obj, to_obj(substs_objs))); + hyps = tail(hyps); + substs = tail(substs); + } + return mk_tactic_success(to_obj(info), new_s); } catch (exception & ex) { return mk_tactic_exception(ex, s); } diff --git a/src/library/vm/vm_list.cpp b/src/library/vm/vm_list.cpp index 698088ab14..253084d657 100644 --- a/src/library/vm/vm_list.cpp +++ b/src/library/vm/vm_list.cpp @@ -32,6 +32,8 @@ vm_obj to_obj(list const & ls) { return list_to_obj(ls); } vm_obj to_obj(list const & ls) { return list_to_obj(ls); } vm_obj to_obj(list const & ls) { return list_to_obj(ls); } +vm_obj to_obj(list> const & ls) { return list_to_obj(ls); } + vm_obj to_obj(buffer const & ls) { return to_obj(to_list(ls)); } vm_obj to_obj(buffer const & ls) { return to_obj(to_list(ls)); } vm_obj to_obj(buffer const & ls) { return to_obj(to_list(ls)); } @@ -106,6 +108,13 @@ vm_obj to_obj(list const & ls) { return to_vm_list(ls, [&](unsigned n) { return mk_vm_nat(n); }); } +vm_obj to_obj(buffer const & ls) { + vm_obj obj = mk_vm_nil(); + for (unsigned i = ls.size(); i > 0; i--) + obj = mk_vm_cons(ls[i - 1], obj); + return obj; +} + void initialize_vm_list() { DECLARE_VM_CASES_BUILTIN(name({"list", "cases_on"}), list_cases_on); } diff --git a/src/library/vm/vm_list.h b/src/library/vm/vm_list.h index 50ff7577e5..4d90fd5c32 100644 --- a/src/library/vm/vm_list.h +++ b/src/library/vm/vm_list.h @@ -26,6 +26,7 @@ list to_list_expr(vm_obj const & o); void to_buffer_expr(vm_obj const & o, buffer & r); vm_obj to_obj(buffer const & ls); vm_obj to_obj(list const & ls); +vm_obj to_obj(list> const & ls); template vm_obj to_vm_list(list const & ls, F const & fn) { @@ -34,6 +35,7 @@ vm_obj to_vm_list(list const & ls, F const & fn) { } vm_obj to_obj(list const & ls); +vm_obj to_obj(buffer const & ls); /* Helper functions for accessing (list A) when A is not expr, name nor level */ inline bool is_nil(vm_obj const & o) { return is_simple(o); } diff --git a/tests/lean/interactive/do_info.lean.expected.out b/tests/lean/interactive/do_info.lean.expected.out index 6a4f97fc5f..16f03b7677 100644 --- a/tests/lean/interactive/do_info.lean.expected.out +++ b/tests/lean/interactive/do_info.lean.expected.out @@ -1,7 +1,7 @@ {"msg":{"caption":"trace output","file_name":"f","pos_col":3,"pos_line":4,"severity":"information","text":"a b c : ℕ,\nh : a = b,\na_1 : c = b\n⊢ a = ?m_1\n\na b c : ℕ,\nh : a = b,\na_1 : c = b\n⊢ ?m_1 = c\n"},"response":"additional_message"} {"message":"file invalidated","response":"ok","seq_num":0} -{"record":{"full-id":"tactic.intro","source":{"column":9,"file":"/library/init/meta/tactic.lean","line":521},"state":"a b c : ℕ\n⊢ a = b → c = b → a = c","type":"name → tactic expr"},"response":"ok","seq_num":6} +{"record":{"full-id":"tactic.intro","source":{"column":9,"file":"/library/init/meta/tactic.lean","line":526},"state":"a b c : ℕ\n⊢ a = b → c = b → a = c","type":"name → tactic expr"},"response":"ok","seq_num":6} {"record":{"full-id":"tactic.transitivity","source":{"column":9,"file":"/library/init/meta/relation_tactics.lean","line":30},"state":"a b c : ℕ\n⊢ a = b → c = b → a = c","type":"tactic unit"},"response":"ok","seq_num":9} -{"record":{"full-id":"tactic.assumption","source":{"column":9,"file":"/library/init/meta/tactic.lean","line":619},"state":"a b c : ℕ\n⊢ a = b → c = b → a = c","type":"tactic unit"},"response":"ok","seq_num":12} +{"record":{"full-id":"tactic.assumption","source":{"column":9,"file":"/library/init/meta/tactic.lean","line":624},"state":"a b c : ℕ\n⊢ a = b → c = b → a = c","type":"tactic unit"},"response":"ok","seq_num":12} {"record":{"full-id":"tactic.symmetry","source":{"column":9,"file":"/library/init/meta/relation_tactics.lean","line":27},"state":"a b c : ℕ\n⊢ a = b → c = b → a = c","type":"tactic unit"},"response":"ok","seq_num":14} -{"record":{"full-id":"tactic.assumption","source":{"column":9,"file":"/library/init/meta/tactic.lean","line":619},"state":"a b c : ℕ\n⊢ a = b → c = b → a = c","type":"tactic unit"},"response":"ok","seq_num":16} +{"record":{"full-id":"tactic.assumption","source":{"column":9,"file":"/library/init/meta/tactic.lean","line":624},"state":"a b c : ℕ\n⊢ a = b → c = b → a = c","type":"tactic unit"},"response":"ok","seq_num":16}