fix(kernel/type_checker): the type checker cache was not taking into account binder information
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
d5184a1751
commit
606e6226c2
7 changed files with 40 additions and 3 deletions
|
@ -137,6 +137,10 @@ void expr_app::dealloc(buffer<expr_cell*> & todelete) {
|
||||||
|
|
||||||
static unsigned dec(unsigned k) { return k == 0 ? 0 : k - 1; }
|
static unsigned dec(unsigned k) { return k == 0 ? 0 : k - 1; }
|
||||||
|
|
||||||
|
bool operator==(expr_binder_info const & i1, expr_binder_info const & i2) {
|
||||||
|
return i1.is_implicit() == i2.is_implicit() && i1.is_cast() == i2.is_cast() && i1.is_contextual() == i2.is_contextual();
|
||||||
|
}
|
||||||
|
|
||||||
// Expr binders (Lambda, Pi)
|
// Expr binders (Lambda, Pi)
|
||||||
expr_binder::expr_binder(expr_kind k, name const & n, expr const & t, expr const & b, expr_binder_info const & i):
|
expr_binder::expr_binder(expr_kind k, name const & n, expr const & t, expr const & b, expr_binder_info const & i):
|
||||||
expr_composite(k, ::lean::hash(t.hash(), b.hash()),
|
expr_composite(k, ::lean::hash(t.hash(), b.hash()),
|
||||||
|
@ -390,6 +394,7 @@ unsigned get_free_var_range(expr const & e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(expr const & a, expr const & b) { return expr_eq_fn()(a, b); }
|
bool operator==(expr const & a, expr const & b) { return expr_eq_fn()(a, b); }
|
||||||
|
bool is_bi_equal(expr const & a, expr const & b) { return expr_eq_fn(true)(a, b); }
|
||||||
|
|
||||||
static expr copy_tag(expr const & e, expr && new_e) {
|
static expr copy_tag(expr const & e, expr && new_e) {
|
||||||
tag t = e.get_tag();
|
tag t = e.get_tag();
|
||||||
|
|
|
@ -150,8 +150,11 @@ public:
|
||||||
|
|
||||||
// =======================================
|
// =======================================
|
||||||
// Structural equality
|
// Structural equality
|
||||||
|
/** \brief Binder information is ignored in the following predicate */
|
||||||
bool operator==(expr const & a, expr const & b);
|
bool operator==(expr const & a, expr const & b);
|
||||||
inline bool operator!=(expr const & a, expr const & b) { return !operator==(a, b); }
|
inline bool operator!=(expr const & a, expr const & b) { return !operator==(a, b); }
|
||||||
|
/** \brief Similar to ==, but it also compares binder information */
|
||||||
|
bool is_bi_equal(expr const & a, expr const & b);
|
||||||
// =======================================
|
// =======================================
|
||||||
|
|
||||||
SPECIALIZE_OPTIONAL_FOR_SMART_PTR(expr)
|
SPECIALIZE_OPTIONAL_FOR_SMART_PTR(expr)
|
||||||
|
@ -232,6 +235,9 @@ public:
|
||||||
bool is_contextual() const { return m_contextual; }
|
bool is_contextual() const { return m_contextual; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool operator==(expr_binder_info const & i1, expr_binder_info const & i2);
|
||||||
|
inline bool operator!=(expr_binder_info const & i1, expr_binder_info const & i2) { return !(i1 == i2); }
|
||||||
|
|
||||||
/** \brief Super class for lambda and pi */
|
/** \brief Super class for lambda and pi */
|
||||||
class expr_binder : public expr_composite {
|
class expr_binder : public expr_composite {
|
||||||
name m_name;
|
name m_name;
|
||||||
|
|
|
@ -39,7 +39,8 @@ bool expr_eq_fn::apply(expr const & a, expr const & b) {
|
||||||
case expr_kind::Lambda: case expr_kind::Pi:
|
case expr_kind::Lambda: case expr_kind::Pi:
|
||||||
return
|
return
|
||||||
apply(binder_domain(a), binder_domain(b)) &&
|
apply(binder_domain(a), binder_domain(b)) &&
|
||||||
apply(binder_body(a), binder_body(b));
|
apply(binder_body(a), binder_body(b)) &&
|
||||||
|
(!m_compare_binder_info || binder_info(a) == binder_info(b));
|
||||||
case expr_kind::Sort:
|
case expr_kind::Sort:
|
||||||
return sort_level(a) == sort_level(b);
|
return sort_level(a) == sort_level(b);
|
||||||
case expr_kind::Macro:
|
case expr_kind::Macro:
|
||||||
|
|
|
@ -15,9 +15,12 @@ namespace lean {
|
||||||
\brief Functional object for comparing expressions.
|
\brief Functional object for comparing expressions.
|
||||||
*/
|
*/
|
||||||
class expr_eq_fn {
|
class expr_eq_fn {
|
||||||
|
bool m_compare_binder_info;
|
||||||
std::unique_ptr<expr_cell_pair_set> m_eq_visited;
|
std::unique_ptr<expr_cell_pair_set> m_eq_visited;
|
||||||
bool apply(expr const & a, expr const & b);
|
bool apply(expr const & a, expr const & b);
|
||||||
public:
|
public:
|
||||||
|
/** \brief If \c is true, then functional object will also compare binder information attached to lambda and Pi expressions */
|
||||||
|
expr_eq_fn(bool c = false):m_compare_binder_info(c) {}
|
||||||
bool operator()(expr const & a, expr const & b) { return apply(a, b); }
|
bool operator()(expr const & a, expr const & b) { return apply(a, b); }
|
||||||
void clear() { m_eq_visited.reset(); }
|
void clear() { m_eq_visited.reset(); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,4 +26,8 @@ using expr_cell_offset_map = typename std::unordered_map<expr_cell_offset, T, ex
|
||||||
// Maps based on structural equality. That is, two keys are equal iff they are structurally equal
|
// Maps based on structural equality. That is, two keys are equal iff they are structurally equal
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using expr_struct_map = typename std::unordered_map<expr, T, expr_hash, std::equal_to<expr>>;
|
using expr_struct_map = typename std::unordered_map<expr, T, expr_hash, std::equal_to<expr>>;
|
||||||
|
// The following map also takes into account binder information
|
||||||
|
struct is_bi_equal_proc { bool operator()(expr const & e1, expr const & e2) const { return is_bi_equal(e1, e2); } };
|
||||||
|
template<typename T>
|
||||||
|
using expr_bi_struct_map = typename std::unordered_map<expr, T, expr_hash, is_bi_equal_proc>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -60,7 +60,11 @@ struct type_checker::imp {
|
||||||
name_generator m_gen;
|
name_generator m_gen;
|
||||||
constraint_handler & m_chandler;
|
constraint_handler & m_chandler;
|
||||||
std::unique_ptr<converter> m_conv;
|
std::unique_ptr<converter> m_conv;
|
||||||
expr_struct_map<expr> m_infer_type_cache;
|
// In the type checker cache, we must take into account binder information.
|
||||||
|
// Examples:
|
||||||
|
// The type of (lambda x : A, t) is (Pi x : A, typeof(t))
|
||||||
|
// The type of (lambda {x : A}, t) is (Pi {x : A}, typeof(t))
|
||||||
|
expr_bi_struct_map<expr> m_infer_type_cache;
|
||||||
converter_context m_conv_ctx;
|
converter_context m_conv_ctx;
|
||||||
type_checker_context m_tc_ctx;
|
type_checker_context m_tc_ctx;
|
||||||
bool m_memoize;
|
bool m_memoize;
|
||||||
|
|
14
tests/lua/tc3.lua
Normal file
14
tests/lua/tc3.lua
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
local env = empty_environment()
|
||||||
|
local t1 = mk_lambda("A", Type, mk_lambda("a", Var(0), Var(0)), binder_info(true))
|
||||||
|
local t2 = mk_lambda("A", Type, mk_lambda("a", Var(0), Var(0)))
|
||||||
|
print(t1)
|
||||||
|
print(t2)
|
||||||
|
local tc = type_checker(env)
|
||||||
|
local T1 = mk_pi("A", Type, mk_arrow(Var(0), Var(1)), binder_info(true))
|
||||||
|
local T2 = mk_pi("A", Type, mk_arrow(Var(0), Var(1)))
|
||||||
|
print(T1)
|
||||||
|
print(T2)
|
||||||
|
print(tc:check(t1))
|
||||||
|
print(tc:check(t2))
|
||||||
|
assert(tc:check(t1):binder_info():is_implicit())
|
||||||
|
assert(not tc:check(t2):binder_info():is_implicit())
|
Loading…
Reference in a new issue