feat(frontends/lean): add auto_param gadget

This commit is contained in:
Leonardo de Moura 2017-02-09 15:49:10 -08:00
parent b13d0e1b4f
commit c8bbb34e2a
8 changed files with 86 additions and 3 deletions

View file

@ -12,6 +12,13 @@ inductive name
| mk_string : string → name → name
| mk_numeral : unsigned → name → name
/-- Gadget for automatic parameter support. This is similar to the opt_param gadget, but it uses
the tactic declaration names tac_name to synthesize the argument.
Like opt_param, this gadget only affects elaboration.
For example, the tactic will *not* be invoked during type class resolution. -/
@[reducible] def {u} auto_param (α : Sort u) (tac_name : name) : Sort u :=
α
instance : inhabited name :=
⟨name.anonymous⟩

View file

@ -1135,6 +1135,14 @@ static optional<expr> is_optional_param(expr const & e) {
}
}
static optional<expr_pair> is_auto_param(expr const & e) {
if (is_app_of(e, get_auto_param_name(), 2)) {
return optional<expr_pair>(app_arg(app_fn(e)), app_arg(e));
} else {
return optional<expr_pair>();
}
}
static optional<expr> is_thunk(expr const & e) {
if (is_app_of(e, get_thunk_name(), 1)) {
return some_expr(app_arg(e));
@ -1150,6 +1158,33 @@ static expr mk_thunk_if_needed(expr const & e, optional<expr> const & is_thunk)
return e;
}
static optional<name> name_lit_to_name(expr const & name_lit) {
if (is_constant(name_lit, get_name_anonymous_name()))
return optional<name>(name());
if (is_app_of(name_lit, get_name_mk_string_name(), 2)) {
if (auto str = to_string(app_arg(app_fn(name_lit))))
if (auto p = name_lit_to_name(app_arg(name_lit)))
return optional<name>(name(*p, str->c_str()));
}
return optional<name>();
}
expr elaborator::mk_auto_param(expr const & name_lit, expr const & expected_type, expr const & ref) {
auto c = name_lit_to_name(name_lit);
if (!c)
throw elaborator_exception(ref, format("invalid auto_param, name literal expected for identifying tactic") +
pp_indent(name_lit));
auto d = m_env.find(*c);
if (!d)
throw elaborator_exception(ref, sstream() << "invalid auto_param, unknown tactic '" << *c << "'");
if (!m_ctx.is_def_eq(d->get_type(), mk_tactic_unit()))
throw elaborator_exception(ref, format("invalid auto_param, invalid tactic '") + format(*c) +
format("' type should be (tactic unit)") +
pp_indent(d->get_type()));
expr t = copy_tag(ref, mk_by(copy_tag(ref, mk_constant(*c))));
return visit(t, some_expr(expected_type));
}
/* Check if fn args resulting type matches the expected type, and fill
first_pass_info & info with information collected in this first pass.
Return true iff the types match.
@ -1168,7 +1203,7 @@ void elaborator::first_pass(expr const & fn, buffer<expr> const & args,
while (is_pi(type)) {
binder_info const & bi = binding_info(type);
expr const & d = binding_domain(type);
if (bi.is_strict_implicit() && i == args.size() && !is_optional_param(d))
if (bi.is_strict_implicit() && i == args.size() && !is_optional_param(d) && !is_auto_param(d))
break;
expr new_arg;
if (!is_explicit(bi)) {
@ -1190,6 +1225,8 @@ void elaborator::first_pass(expr const & fn, buffer<expr> const & args,
info.new_instances_size.push_back(info.new_instances.size());
} else if (auto def_value = is_optional_param(d)) {
new_arg = *def_value;
} else if (auto p = is_auto_param(d)) {
new_arg = mk_auto_param(p->second, p->first, ref);
} else {
break;
}
@ -1304,7 +1341,7 @@ expr elaborator::visit_base_app_simple(expr const & _fn, arg_mask amask, buffer<
binder_info const & bi = binding_info(type);
expr const & d = binding_domain(type);
expr new_arg;
if (amask == arg_mask::Default && bi.is_strict_implicit() && i == args.size() && !is_optional_param(d))
if (amask == arg_mask::Default && bi.is_strict_implicit() && i == args.size() && !is_optional_param(d) && !is_auto_param(d))
break;
if ((amask == arg_mask::Default && !is_explicit(bi)) ||
(amask == arg_mask::InstHoExplicit && !is_explicit(bi) && !bi.is_inst_implicit() && !is_pi(d))) {
@ -1346,6 +1383,8 @@ expr elaborator::visit_base_app_simple(expr const & _fn, arg_mask amask, buffer<
i++;
} else if (auto def_value = is_optional_param(d)) {
new_arg = *def_value;
} else if (auto p = is_auto_param(d)) {
new_arg = mk_auto_param(p->second, p->first, ref);
} else {
break;
}

View file

@ -252,6 +252,8 @@ private:
void finalize_core(sanitize_param_names_fn & S, buffer<expr> & es,
bool check_unassigned, bool to_simple_metavar, bool collect_local_ctx);
expr mk_auto_param(expr const & name_lit, expr const & expected_type, expr const & ref);
public:
elaborator(environment const & env, options const & opts, name const & decl_name,
metavar_context const & mctx, local_context const & lctx,

View file

@ -338,7 +338,7 @@ pair<name, option_kind> parse_option_name(parser & p, char const * error_msg) {
}
expr mk_tactic_unit() {
return mk_app(mk_constant(get_tactic_name(), {mk_level_one()}), mk_constant(get_unit_name()));
return mk_app(mk_constant(get_tactic_name(), {mk_level_zero()}), mk_constant(get_unit_name()));
}
expr quote_name(name const & n) {

View file

@ -15,6 +15,7 @@ name const * g_and = nullptr;
name const * g_and_elim_left = nullptr;
name const * g_and_elim_right = nullptr;
name const * g_and_intro = nullptr;
name const * g_auto_param = nullptr;
name const * g_bit0 = nullptr;
name const * g_bit1 = nullptr;
name const * g_bool = nullptr;
@ -495,6 +496,7 @@ void initialize_constants() {
g_and_elim_left = new name{"and", "elim_left"};
g_and_elim_right = new name{"and", "elim_right"};
g_and_intro = new name{"and", "intro"};
g_auto_param = new name{"auto_param"};
g_bit0 = new name{"bit0"};
g_bit1 = new name{"bit1"};
g_bool = new name{"bool"};
@ -976,6 +978,7 @@ void finalize_constants() {
delete g_and_elim_left;
delete g_and_elim_right;
delete g_and_intro;
delete g_auto_param;
delete g_bit0;
delete g_bit1;
delete g_bool;
@ -1456,6 +1459,7 @@ name const & get_and_name() { return *g_and; }
name const & get_and_elim_left_name() { return *g_and_elim_left; }
name const & get_and_elim_right_name() { return *g_and_elim_right; }
name const & get_and_intro_name() { return *g_and_intro; }
name const & get_auto_param_name() { return *g_auto_param; }
name const & get_bit0_name() { return *g_bit0; }
name const & get_bit1_name() { return *g_bit1; }
name const & get_bool_name() { return *g_bool; }

View file

@ -17,6 +17,7 @@ name const & get_and_name();
name const & get_and_elim_left_name();
name const & get_and_elim_right_name();
name const & get_and_intro_name();
name const & get_auto_param_name();
name const & get_bit0_name();
name const & get_bit1_name();
name const & get_bool_name();

View file

@ -10,6 +10,7 @@ and
and.elim_left
and.elim_right
and.intro
auto_param
bit0
bit1
bool

View file

@ -0,0 +1,29 @@
open tactic
meta def my_tac : tactic unit :=
assumption <|> abstract (comp_val >> skip) <|> fail "my_tac failed to synthesize auto_param"
def f (x : nat) (h : auto_param (x > 0) `my_tac) : nat :=
nat.pred x
check f 12
check f 13
lemma f_inj {x₁ x₂ : nat} {h₁ : x₁ > 0} {h₂ : x₂ > 0} : f x₁ = f x₂ → x₁ = x₂ :=
begin
unfold f, intro h,
cases x₁,
exact absurd h₁ (lt_irrefl _),
cases x₂,
exact absurd h₂ (lt_irrefl _),
apply congr_arg nat.succ,
assumption
end
check @f_inj
lemma f_def {x : nat} (h : x > 0) : f x = nat.pred x :=
rfl
-- The following is an error
-- check λ x, f x