feat(library/app_builder): new app_builder on top of type_context
The new version is more robust, and invokes type class resolution if needed.
This commit is contained in:
parent
87ec7383dd
commit
ee0974650a
6 changed files with 245 additions and 231 deletions
|
@ -1141,22 +1141,26 @@ static environment relevant_thms_cmd(parser & p) {
|
|||
|
||||
static environment app_builder_cmd(parser & p) {
|
||||
environment const & env = p.env();
|
||||
type_checker tc(env);
|
||||
app_builder b(tc);
|
||||
auto pos = p.pos();
|
||||
app_builder b(env);
|
||||
name c = p.check_constant_next("invalid #app_builder command, constant expected");
|
||||
p.check_token_next(get_lparen_tk(), "invalid #app_builder command, '(' expected");
|
||||
buffer<expr> args;
|
||||
while (true) {
|
||||
args.push_back(p.parse_expr());
|
||||
expr e; level_param_names ls;
|
||||
std::tie(e, ls) = parse_local_expr(p);
|
||||
args.push_back(e);
|
||||
if (!p.curr_is_token(get_comma_tk()))
|
||||
break;
|
||||
p.next();
|
||||
}
|
||||
p.check_token_next(get_rparen_tk(), "invalid #app_builder command, ')' expected");
|
||||
if (auto r = b.mk_app(c, args.size(), args.data())) {
|
||||
p.regular_stream() << *r << "\n";
|
||||
type_checker tc(env);
|
||||
expr t = tc.check_ignore_undefined_universes(*r).first;
|
||||
p.regular_stream() << *r << " : " << t << "\n";
|
||||
} else {
|
||||
p.regular_stream() << "<failed>\n";
|
||||
throw parser_error(sstream() << "failed to build application for '" << c << "'", pos);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
|
|
@ -10,241 +10,212 @@ Author: Leonardo de Moura
|
|||
#include "library/match.h"
|
||||
#include "library/app_builder.h"
|
||||
#include "library/kernel_bindings.h"
|
||||
#include "library/tmp_type_context.h"
|
||||
|
||||
namespace lean {
|
||||
|
||||
struct app_builder::imp {
|
||||
// For each declaration we associate the number of explicit arguments provided to
|
||||
// it, and which of them are used to infer the implicit arguments.
|
||||
struct decl_info {
|
||||
unsigned m_nargs; // total number of explicit arguments
|
||||
list<unsigned> m_used_idxs; // which ones are used to infer implicit arguments
|
||||
decl_info(unsigned nargs, list<unsigned> const & used_idxs):
|
||||
m_nargs(nargs), m_used_idxs(used_idxs) {}
|
||||
decl_info() {}
|
||||
tmp_type_context m_ctx;
|
||||
|
||||
struct entry {
|
||||
unsigned m_num_umeta;
|
||||
unsigned m_num_emeta;
|
||||
expr m_app;
|
||||
list<optional<expr>> m_inst_args; // "mask" of implicit instance arguments
|
||||
list<expr> m_expl_args; // metavars for explicit arguments
|
||||
|
||||
/*
|
||||
IMPORTANT: for m_inst_args we store the arguments in reverse order.
|
||||
For example, the first element in the list indicates whether the last argument
|
||||
is an instance implicit argument or not. If it is not none, then the element
|
||||
is the associated metavariable
|
||||
|
||||
m_expl_args are also stored in reverse order
|
||||
*/
|
||||
};
|
||||
|
||||
struct cache_key {
|
||||
struct key {
|
||||
name m_name;
|
||||
list<expr> m_arg_types;
|
||||
unsigned m_num_expl;
|
||||
unsigned m_hash;
|
||||
cache_key(name const & n, unsigned num_arg_types, expr const * arg_types):
|
||||
m_name(n), m_arg_types(to_list(arg_types, arg_types + num_arg_types)) {
|
||||
m_hash = m_name.hash();
|
||||
for (unsigned i = 0; i < num_arg_types; i++)
|
||||
m_hash = hash(m_hash, arg_types[i].hash());
|
||||
}
|
||||
};
|
||||
struct cache_key_hash_fn {
|
||||
unsigned operator()(cache_key const & e) const { return e.m_hash; }
|
||||
};
|
||||
struct cache_key_equal_fn {
|
||||
bool operator()(cache_key const & e1, cache_key const & e2) const {
|
||||
return
|
||||
e1.m_name == e2.m_name &&
|
||||
e1.m_arg_types == e2.m_arg_types;
|
||||
}
|
||||
};
|
||||
// If nil, then the mask is composed of the last m_num_expl arguments.
|
||||
// If nonnil, then the mask is NOT of the form [false*, true*]
|
||||
list<bool> m_mask;
|
||||
|
||||
// The cache stores a mapping (decl + type of explicit arguments ==> term t).
|
||||
// If t is closed term, then we obtain the final application by using
|
||||
// mk_app(t, explicit_args)
|
||||
// If t contains free variables, then we obtain the final application by using
|
||||
// instantiate(t, explicit_args)
|
||||
typedef scoped_map<cache_key, expr, cache_key_hash_fn, cache_key_equal_fn> cache;
|
||||
|
||||
type_checker & m_tc;
|
||||
whnf_match_plugin m_plugin;
|
||||
name_map<decl_info> m_decl_info;
|
||||
cache m_cache;
|
||||
buffer<levels> m_levels;
|
||||
|
||||
imp(type_checker & tc):m_tc(tc), m_plugin(tc) {
|
||||
m_levels.push_back(levels());
|
||||
}
|
||||
|
||||
// Make sure m_levels contains at least nlvls metavariable universe levels
|
||||
void ensure_levels(unsigned nlvls) {
|
||||
while (m_levels.size() <= nlvls) {
|
||||
level new_lvl = mk_idx_metauniv(m_levels.size() - 1);
|
||||
levels new_lvls = append(m_levels.back(), levels(new_lvl));
|
||||
m_levels.push_back(new_lvls);
|
||||
}
|
||||
}
|
||||
|
||||
// We say the given mask is simple if it is of the form (false*, true*).
|
||||
// That is, a block of false followed by a blocked of true
|
||||
static bool is_simple_mask(buffer<bool> & explicit_mask) {
|
||||
static bool is_simple(list<bool> const & mask) {
|
||||
bool found_true = false;
|
||||
for (bool const & b : explicit_mask) {
|
||||
if (b)
|
||||
for (bool b : mask) {
|
||||
if (b) {
|
||||
found_true = true;
|
||||
else if (found_true)
|
||||
} else if (found_true) {
|
||||
// found (true, false)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
key(name const & c, unsigned n):
|
||||
m_name(c), m_num_expl(n),
|
||||
m_hash(::lean::hash(c.hash(), n)) {
|
||||
}
|
||||
|
||||
key(name const & c, list<bool> const & m):
|
||||
m_name(c), m_num_expl(length(m)) {
|
||||
m_hash = ::lean::hash(c.hash(), m_num_expl);
|
||||
if (!is_simple(m)) {
|
||||
m_mask = m;
|
||||
for (bool b : m) {
|
||||
if (b)
|
||||
m_hash = ::lean::hash(m_hash, 17u);
|
||||
else
|
||||
m_hash = ::lean::hash(m_hash, 31u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool check_invariant() const {
|
||||
lean_assert(empty(m_mask) || length(m_mask) == m_num_expl);
|
||||
lean_assert(empty(m_mask) || !is_simple(m_mask));
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned hash() const {
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
friend bool operator==(key const & k1, key const & k2) {
|
||||
return k1.m_name == k2.m_name && k1.m_num_expl == k2.m_num_expl && k1.m_mask == k2.m_mask;
|
||||
}
|
||||
};
|
||||
|
||||
struct key_hash_fn {
|
||||
unsigned operator()(key const & k) const { return k.hash(); }
|
||||
};
|
||||
|
||||
typedef std::unordered_map<key, entry, key_hash_fn> map;
|
||||
|
||||
map m_map;
|
||||
|
||||
imp(environment const & env, io_state const & ios, reducible_behavior b):
|
||||
m_ctx(env, ios, b) {
|
||||
}
|
||||
|
||||
levels mk_metavars(declaration const & d, buffer<expr> & mvars, buffer<optional<expr>> & inst_args) {
|
||||
m_ctx.clear();
|
||||
unsigned num_univ = d.get_num_univ_params();
|
||||
buffer<level> lvls_buffer;
|
||||
for (unsigned i = 0; i < num_univ; i++) {
|
||||
lvls_buffer.push_back(m_ctx.mk_uvar());
|
||||
}
|
||||
levels lvls = to_list(lvls_buffer);
|
||||
expr type = m_ctx.whnf(instantiate_type_univ_params(d, lvls));
|
||||
while (is_pi(type)) {
|
||||
expr mvar = m_ctx.mk_mvar(binding_domain(type));
|
||||
if (binding_info(type).is_inst_implicit())
|
||||
inst_args.push_back(some_expr(mvar));
|
||||
else
|
||||
inst_args.push_back(none_expr());
|
||||
mvars.push_back(mvar);
|
||||
type = m_ctx.whnf(instantiate(binding_body(type), mvar));
|
||||
}
|
||||
return lvls;
|
||||
}
|
||||
|
||||
optional<entry> get_entry(name const & c, unsigned nargs) {
|
||||
key k(c, nargs);
|
||||
lean_assert(k.check_invariant());
|
||||
auto it = m_map.find(k);
|
||||
if (it == m_map.end()) {
|
||||
if (auto d = m_ctx.env().find(c)) {
|
||||
buffer<expr> mvars;
|
||||
buffer<optional<expr>> inst_args;
|
||||
levels lvls = mk_metavars(*d, mvars, inst_args);
|
||||
if (nargs > mvars.size())
|
||||
return optional<entry>(); // insufficient number of arguments
|
||||
entry e;
|
||||
e.m_num_umeta = d->get_num_univ_params();
|
||||
e.m_num_emeta = mvars.size();
|
||||
e.m_app = ::lean::mk_app(mk_constant(c, lvls), mvars);
|
||||
e.m_inst_args = reverse_to_list(inst_args.begin(), inst_args.end());
|
||||
e.m_expl_args = reverse_to_list(mvars.begin() + mvars.size() - nargs, mvars.end());
|
||||
m_map.insert(mk_pair(k, e));
|
||||
return optional<entry>(e);
|
||||
} else {
|
||||
return optional<entry>(); // unknown decl
|
||||
}
|
||||
} else {
|
||||
return optional<entry>(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
bool check_all_assigned(entry const & e) {
|
||||
lean_assert(e.m_num_emeta == length(e.m_inst_args));
|
||||
// recall that the flags at e.m_inst_args are stored in reverse order.
|
||||
// For example, the first flag in the list indicates whether the last argument
|
||||
// is an instance implicit argument or not.
|
||||
unsigned i = e.m_num_emeta;
|
||||
for (optional<expr> const & inst_arg : e.m_inst_args) {
|
||||
lean_assert(i > 0);
|
||||
--i;
|
||||
if (inst_arg) {
|
||||
expr type = m_ctx.instantiate_uvars_mvars(mlocal_type(*inst_arg));
|
||||
if (auto v = m_ctx.mk_class_instance(type)) {
|
||||
if (!m_ctx.force_assign(*inst_arg, *v))
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!m_ctx.is_mvar_assigned(i))
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = 0; i < e.m_num_umeta; i++) {
|
||||
if (!m_ctx.is_uvar_assigned(i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void save_decl_info(declaration const & d, unsigned nargs, buffer<unsigned> const & used_idxs) {
|
||||
if (!m_decl_info.contains(d.get_name())) {
|
||||
m_decl_info.insert(d.get_name(), decl_info(nargs, to_list(used_idxs)));
|
||||
}
|
||||
}
|
||||
|
||||
optional<expr> mk_app_core(declaration const & d, unsigned nargs, expr const * args, bool use_cache) {
|
||||
unsigned num_univs = d.get_num_univ_params();
|
||||
ensure_levels(num_univs);
|
||||
expr type = instantiate_type_univ_params(d, m_levels[num_univs]);
|
||||
buffer<optional<level>> lsubst;
|
||||
buffer<optional<expr>> esubst;
|
||||
lsubst.resize(num_univs, none_level());
|
||||
constraint_seq cs;
|
||||
buffer<unsigned> used_idxs;
|
||||
buffer<expr> used_types;
|
||||
buffer<bool> explicit_mask;
|
||||
buffer<expr> domain_types;
|
||||
unsigned idx = 0;
|
||||
while (is_pi(type)) {
|
||||
explicit_mask.push_back(is_explicit(binding_info(type)));
|
||||
esubst.push_back(none_expr());
|
||||
domain_types.push_back(binding_domain(type));
|
||||
// TODO(Leo): perhaps, we should cache the result of this while-loop.
|
||||
// The result of this computation can be reused in future calls.
|
||||
expr meta = mk_idx_metavar(idx, binding_domain(type));
|
||||
idx++;
|
||||
type = instantiate(binding_body(type), meta);
|
||||
}
|
||||
unsigned i = domain_types.size();
|
||||
unsigned j = nargs;
|
||||
while (i > 0) {
|
||||
optional<expr> mk_app(name const & c, unsigned nargs, expr const * args) {
|
||||
optional<entry> e = get_entry(c, nargs);
|
||||
if (!e)
|
||||
return none_expr();
|
||||
m_ctx.clear();
|
||||
m_ctx.set_next_uvar_idx(e->m_num_umeta);
|
||||
m_ctx.set_next_mvar_idx(e->m_num_emeta);
|
||||
unsigned i = nargs;
|
||||
for (auto m : e->m_expl_args) {
|
||||
if (i == 0)
|
||||
return none_expr();
|
||||
--i;
|
||||
if (explicit_mask[i]) {
|
||||
if (j == 0)
|
||||
return none_expr();
|
||||
--j;
|
||||
expr arg_type = m_tc.infer(args[j], cs);
|
||||
if (cs)
|
||||
return none_expr();
|
||||
bool assigned = false;
|
||||
if (!match(domain_types[i], arg_type, lsubst, esubst,
|
||||
nullptr, nullptr, &m_plugin, &assigned))
|
||||
return none_expr();
|
||||
if (assigned && use_cache) {
|
||||
used_idxs.push_back(j);
|
||||
used_types.push_back(arg_type);
|
||||
}
|
||||
esubst[i] = some_expr(args[j]);
|
||||
} else {
|
||||
if (!esubst[i])
|
||||
return none_expr();
|
||||
expr arg_type = m_tc.infer(*esubst[i], cs);
|
||||
if (cs)
|
||||
return none_expr();
|
||||
if (!match(domain_types[i], arg_type, lsubst, esubst,
|
||||
nullptr, nullptr, &m_plugin))
|
||||
if (!m_ctx.assign(m, args[i]))
|
||||
return none_expr();
|
||||
}
|
||||
}
|
||||
bool has_unassigned_lvls = std::find(lsubst.begin(), lsubst.end(), none_level()) != lsubst.end();
|
||||
if (j > 0 || has_unassigned_lvls)
|
||||
if (!check_all_assigned(*e))
|
||||
return none_expr();
|
||||
if (use_cache)
|
||||
save_decl_info(d, nargs, used_idxs);
|
||||
buffer<level> r_lvls;
|
||||
for (optional<level> const & l : lsubst)
|
||||
r_lvls.push_back(*l);
|
||||
buffer<expr> r_args;
|
||||
for (optional<expr> const & o : esubst)
|
||||
r_args.push_back(*o);
|
||||
lean_assert(explicit_mask.size() == r_args.size());
|
||||
if (!use_cache) {
|
||||
return some_expr(::lean::mk_app(mk_constant(d.get_name(), to_list(r_lvls)), r_args.size(), r_args.data()));
|
||||
} else if (is_simple_mask(explicit_mask)) {
|
||||
expr f = ::lean::mk_app(mk_constant(d.get_name(), to_list(r_lvls)), r_args.size() - nargs, r_args.data());
|
||||
if (use_cache) {
|
||||
cache_key k(d.get_name(), used_types.size(), used_types.data());
|
||||
m_cache.insert(k, f);
|
||||
}
|
||||
return some_expr(::lean::mk_app(f, nargs, r_args.end() - nargs));
|
||||
} else {
|
||||
buffer<expr> imp_args;
|
||||
buffer<expr> expl_args;
|
||||
for (unsigned i = 0; i < explicit_mask.size(); i++) {
|
||||
if (explicit_mask[i]) {
|
||||
imp_args.push_back(mk_var(expl_args.size()));
|
||||
expl_args.push_back(r_args[i]);
|
||||
} else {
|
||||
imp_args.push_back(r_args[i]);
|
||||
}
|
||||
}
|
||||
expr f = ::lean::mk_app(mk_constant(d.get_name(), to_list(r_lvls)), imp_args.size(), imp_args.data());
|
||||
if (use_cache) {
|
||||
cache_key k(d.get_name(), used_types.size(), used_types.data());
|
||||
m_cache.insert(k, f);
|
||||
}
|
||||
return some_expr(instantiate(f, expl_args.size(), expl_args.data()));
|
||||
}
|
||||
return some_expr(m_ctx.instantiate_uvars_mvars(e->m_app));
|
||||
}
|
||||
|
||||
optional<expr> mk_app(declaration const & d, unsigned nargs, expr const * args, bool use_cache) {
|
||||
if (use_cache) {
|
||||
if (auto info = m_decl_info.find(d.get_name())) {
|
||||
if (info->m_nargs != nargs)
|
||||
optional<expr> mk_app(name const & /* c */, unsigned /* mask_sz */, bool const * /* mask */, expr const * /* args */) {
|
||||
return none_expr();
|
||||
buffer<expr> arg_types;
|
||||
constraint_seq cs;
|
||||
for (unsigned idx : info->m_used_idxs) {
|
||||
lean_assert(idx < nargs);
|
||||
expr t = m_tc.infer(args[idx], cs);
|
||||
if (cs)
|
||||
return none_expr(); // constraint was generated
|
||||
arg_types.push_back(t);
|
||||
}
|
||||
cache_key k(d.get_name(), arg_types.size(), arg_types.data());
|
||||
auto it = m_cache.find(k);
|
||||
if (it != m_cache.end()) {
|
||||
if (closed(it->second))
|
||||
return some_expr(::lean::mk_app(it->second, nargs, args));
|
||||
else
|
||||
return some_expr(instantiate(it->second, nargs, args));
|
||||
} else {
|
||||
return mk_app_core(d, nargs, args, use_cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mk_app_core(d, nargs, args, use_cache);
|
||||
}
|
||||
|
||||
void push() {
|
||||
m_cache.push();
|
||||
}
|
||||
|
||||
void pop() {
|
||||
m_cache.pop();
|
||||
}
|
||||
};
|
||||
|
||||
app_builder::app_builder(type_checker & tc):m_ptr(new imp(tc)) {}
|
||||
app_builder::app_builder(environment const & env, io_state const & ios, reducible_behavior b):
|
||||
m_ptr(new imp(env, ios, b)) {
|
||||
}
|
||||
|
||||
app_builder::app_builder(environment const & env, reducible_behavior b):
|
||||
app_builder(env, get_dummy_ios(), b) {
|
||||
}
|
||||
|
||||
app_builder::~app_builder() {}
|
||||
optional<expr> app_builder::mk_app(declaration const & d, unsigned nargs, expr const * args, bool use_cache) {
|
||||
return m_ptr->mk_app(d, nargs, args, use_cache);
|
||||
|
||||
optional<expr> app_builder::mk_app(name const & c, unsigned nargs, expr const * args) {
|
||||
return m_ptr->mk_app(c, nargs, args);
|
||||
}
|
||||
optional<expr> app_builder::mk_app(name const & n, unsigned nargs, expr const * args, bool use_cache) {
|
||||
declaration const & d = m_ptr->m_tc.env().get(n);
|
||||
return mk_app(d, nargs, args, use_cache);
|
||||
|
||||
optional<expr> app_builder::mk_app(name const & c, unsigned mask_sz, bool const * mask, expr const * args) {
|
||||
return m_ptr->mk_app(c, mask_sz, mask, args);
|
||||
}
|
||||
optional<expr> app_builder::mk_app(name const & n, std::initializer_list<expr> const & args, bool use_cache) {
|
||||
return mk_app(n, args.size(), args.begin(), use_cache);
|
||||
}
|
||||
optional<expr> app_builder::mk_app(name const & n, expr const & a1, bool use_cache) {
|
||||
return mk_app(n, {a1}, use_cache);
|
||||
}
|
||||
optional<expr> app_builder::mk_app(name const & n, expr const & a1, expr const & a2, bool use_cache) {
|
||||
return mk_app(n, {a1, a2}, use_cache);
|
||||
}
|
||||
optional<expr> app_builder::mk_app(name const & n, expr const & a1, expr const & a2, expr const & a3, bool use_cache) {
|
||||
return mk_app(n, {a1, a2, a3}, use_cache);
|
||||
}
|
||||
void app_builder::push() { m_ptr->push(); }
|
||||
void app_builder::pop() { m_ptr->pop(); }
|
||||
}
|
||||
|
|
|
@ -6,7 +6,9 @@ Author: Leonardo de Moura
|
|||
*/
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "kernel/type_checker.h"
|
||||
#include "kernel/environment.h"
|
||||
#include "library/io_state.h"
|
||||
#include "library/reducible.h"
|
||||
|
||||
namespace lean {
|
||||
/** \brief Helper for creating simple applications where some arguments are inferred using
|
||||
|
@ -27,7 +29,8 @@ class app_builder {
|
|||
struct imp;
|
||||
std::unique_ptr<imp> m_ptr;
|
||||
public:
|
||||
app_builder(type_checker & tc);
|
||||
app_builder(environment const & env, io_state const & ios, reducible_behavior b = UnfoldReducible);
|
||||
app_builder(environment const & env, reducible_behavior b = UnfoldReducible);
|
||||
~app_builder();
|
||||
/** \brief Create an application (d.{_ ... _} _ ... _ args[0] ... args[nargs-1]).
|
||||
The missing arguments and universes levels are inferred using type inference.
|
||||
|
@ -38,21 +41,24 @@ public:
|
|||
|
||||
\remark This methods uses just higher-order pattern matching.
|
||||
*/
|
||||
optional<expr> mk_app(declaration const & d, unsigned nargs, expr const * args, bool use_cache = true);
|
||||
optional<expr> mk_app(name const & n, unsigned nargs, expr const * args, bool use_cache = true);
|
||||
optional<expr> mk_app(name const & n, std::initializer_list<expr> const & args, bool use_cache = true);
|
||||
optional<expr> mk_app(name const & n, expr const & a1, bool use_cache = true);
|
||||
optional<expr> mk_app(name const & n, expr const & a1, expr const & a2, bool use_cache = true);
|
||||
optional<expr> mk_app(name const & n, expr const & a1, expr const & a2, expr const & a3, bool use_cache = true);
|
||||
/** \brief Create a backtracking point for cached information.
|
||||
\remark This method does not invoke tc->push()
|
||||
*/
|
||||
void push();
|
||||
/** \brief Restore backtracking point, values cached between this push and the corresponding pop
|
||||
are removed from the cache.
|
||||
optional<expr> mk_app(name const & c, unsigned nargs, expr const * args);
|
||||
|
||||
\remark This method does not invoke tc->pop()
|
||||
*/
|
||||
void pop();
|
||||
optional<expr> mk_app(name const & c, std::initializer_list<expr> const & args) {
|
||||
return mk_app(c, args.size(), args.begin());
|
||||
}
|
||||
|
||||
optional<expr> mk_app(name const & c, expr const & a1) {
|
||||
return mk_app(c, {a1});
|
||||
}
|
||||
|
||||
optional<expr> mk_app(name const & c, expr const & a1, expr const & a2) {
|
||||
return mk_app(c, {a1, a2});
|
||||
}
|
||||
|
||||
optional<expr> mk_app(name const & c, expr const & a1, expr const & a2, expr const & a3) {
|
||||
return mk_app(c, {a1, a2, a3});
|
||||
}
|
||||
|
||||
optional<expr> mk_app(name const & c, unsigned mask_sz, bool const * mask, expr const * args);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -722,6 +722,19 @@ bool type_context::process_assignment(expr const & ma, expr const & v) {
|
|||
}
|
||||
}
|
||||
|
||||
bool type_context::assign(expr const & ma, expr const & v) {
|
||||
expr const & f = get_app_fn(ma);
|
||||
if (is_assigned(f)) {
|
||||
return is_def_eq(ma, v);
|
||||
} else {
|
||||
return process_assignment(ma, v);
|
||||
}
|
||||
}
|
||||
|
||||
bool type_context::force_assign(expr const & ma, expr const & v) {
|
||||
return process_assignment(ma, v);
|
||||
}
|
||||
|
||||
/** \brief This is an auxiliary method for is_def_eq. It handles the "easy cases". */
|
||||
lbool type_context::quick_is_def_eq(expr const & e1, expr const & e2) {
|
||||
if (e1 == e2)
|
||||
|
|
|
@ -334,6 +334,20 @@ public:
|
|||
optional<expr> mk_class_instance(expr const & type);
|
||||
optional<expr> next_class_instance();
|
||||
|
||||
/** \brief Given \c ma of the form <tt>?m t_1 ... t_n</tt>, (try to) assign
|
||||
?m to (an abstraction of) v. Return true if success and false otherwise.
|
||||
|
||||
\remark If ?m is already assigned, we just check if ma and v are definitionally
|
||||
equal. */
|
||||
bool assign(expr const & ma, expr const & v);
|
||||
|
||||
/** \brief Similar to \c assign, but it replaces the existing assignment
|
||||
if the metavariable is already assigned.
|
||||
|
||||
Application: for implicit instance arguments, we want the term to be the one
|
||||
generated by type class resolution even when it can be inferred by type inference. */
|
||||
bool force_assign(expr const & ma, expr const & v);
|
||||
|
||||
/** \brief Clear internal caches used to speedup computation */
|
||||
void clear_cache();
|
||||
|
||||
|
|
|
@ -9,10 +9,16 @@ set_option pp.all true
|
|||
#app_builder eq.trans (H1, H2)
|
||||
#app_builder eq.symm (H1)
|
||||
|
||||
open algebra
|
||||
variables A : Type
|
||||
variables [s : comm_ring A]
|
||||
open algebra nat
|
||||
universe l
|
||||
constant A : Type.{l}
|
||||
constant s : comm_ring A
|
||||
attribute s [instance]
|
||||
variables x y : A
|
||||
|
||||
#app_builder eq.refl (s)
|
||||
#app_builder eq.refl (x)
|
||||
#app_builder add (x, y)
|
||||
#app_builder add (a, b)
|
||||
#app_builder mul (a, b)
|
||||
#app_builder sub (x, y)
|
||||
|
|
Loading…
Reference in a new issue