feat(kernel): add (optional) field m_type to expr_const, this field is useful for implementing the tactic framework

This field should not be visible in the external API.

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-11-19 11:21:45 -08:00
parent 9bafa5a9e8
commit 7f088b7635
14 changed files with 167 additions and 29 deletions

View file

@ -1,7 +1,7 @@
add_library(kernel expr.cpp free_vars.cpp abstract.cpp instantiate.cpp add_library(kernel expr.cpp free_vars.cpp abstract.cpp instantiate.cpp
normalizer.cpp context.cpp level.cpp object.cpp environment.cpp normalizer.cpp context.cpp level.cpp object.cpp environment.cpp
type_checker.cpp builtin.cpp occurs.cpp metavar.cpp justification.cpp type_checker.cpp builtin.cpp occurs.cpp metavar.cpp justification.cpp
unification_constraint.cpp printer.cpp formatter.cpp unification_constraint.cpp printer.cpp formatter.cpp has_cached_type.cpp
kernel_exception.cpp type_checker_justification.cpp pos_info_provider.cpp kernel_exception.cpp type_checker_justification.cpp pos_info_provider.cpp
replace_visitor.cpp) replace_visitor.cpp)

View file

@ -20,6 +20,7 @@ Author: Leonardo de Moura
#include "kernel/threadsafe_environment.h" #include "kernel/threadsafe_environment.h"
#include "kernel/type_checker.h" #include "kernel/type_checker.h"
#include "kernel/normalizer.h" #include "kernel/normalizer.h"
#include "kernel/has_cached_type.h"
namespace lean { namespace lean {
static name g_builtin_module("builtin_module"); static name g_builtin_module("builtin_module");
@ -287,6 +288,11 @@ struct environment::imp {
m_uvars.emplace_back(); m_uvars.emplace_back();
} }
void check_no_cached_type(expr const & e, environment const & env) {
if (has_cached_type(e))
throw kernel_exception(env, "expression has a constant with a cached type, this is a bug in one of Lean tactics and/or solvers");
}
/** /**
\brief Throw an exception if \c t is not a type or type of \c \brief Throw an exception if \c t is not a type or type of \c
v is not convertible to \c t. v is not convertible to \c t.
@ -331,6 +337,8 @@ struct environment::imp {
/** \brief Add new definition. */ /** \brief Add new definition. */
void add_definition(name const & n, expr const & t, expr const & v, bool opaque, environment const & env) { void add_definition(name const & n, expr const & t, expr const & v, bool opaque, environment const & env) {
check_no_cached_type(t, env);
check_no_cached_type(v, env);
check_new_definition(n, t, v, env); check_new_definition(n, t, v, env);
unsigned w = get_max_weight(v) + 1; unsigned w = get_max_weight(v) + 1;
register_named_object(mk_definition(n, t, v, opaque, w)); register_named_object(mk_definition(n, t, v, opaque, w));
@ -341,6 +349,7 @@ struct environment::imp {
The type of the new definition is the type of \c v. The type of the new definition is the type of \c v.
*/ */
void add_definition(name const & n, expr const & v, bool opaque, environment const & env) { void add_definition(name const & n, expr const & v, bool opaque, environment const & env) {
check_no_cached_type(v, env);
check_name(n, env); check_name(n, env);
expr v_t = m_type_checker->infer_type(v); expr v_t = m_type_checker->infer_type(v);
unsigned w = get_max_weight(v) + 1; unsigned w = get_max_weight(v) + 1;
@ -349,12 +358,15 @@ struct environment::imp {
/** \brief Add new theorem. */ /** \brief Add new theorem. */
void add_theorem(name const & n, expr const & t, expr const & v, environment const & env) { void add_theorem(name const & n, expr const & t, expr const & v, environment const & env) {
check_no_cached_type(t, env);
check_no_cached_type(v, env);
check_new_definition(n, t, v, env); check_new_definition(n, t, v, env);
register_named_object(mk_theorem(n, t, v)); register_named_object(mk_theorem(n, t, v));
} }
/** \brief Add new axiom. */ /** \brief Add new axiom. */
void add_axiom(name const & n, expr const & t, environment const & env) { void add_axiom(name const & n, expr const & t, environment const & env) {
check_no_cached_type(t, env);
check_name(n, env); check_name(n, env);
m_type_checker->check_type(t); m_type_checker->check_type(t);
register_named_object(mk_axiom(n, t)); register_named_object(mk_axiom(n, t));
@ -362,6 +374,7 @@ struct environment::imp {
/** \brief Add new variable. */ /** \brief Add new variable. */
void add_var(name const & n, expr const & t, environment const & env) { void add_var(name const & n, expr const & t, environment const & env) {
check_no_cached_type(t, env);
check_name(n, env); check_name(n, env);
m_type_checker->check_type(t); m_type_checker->check_type(t);
register_named_object(mk_var_decl(n, t)); register_named_object(mk_var_decl(n, t));

View file

@ -67,9 +67,14 @@ expr_var::expr_var(unsigned idx):
expr_cell(expr_kind::Var, idx, false), expr_cell(expr_kind::Var, idx, false),
m_vidx(idx) {} m_vidx(idx) {}
expr_const::expr_const(name const & n): expr_const::expr_const(name const & n, expr const & t):
expr_cell(expr_kind::Constant, n.hash(), false), expr_cell(expr_kind::Constant, n.hash(), t && t.has_metavar()),
m_name(n) {} m_name(n),
m_type(t) {}
void expr_const::dealloc(buffer<expr_cell*> & todelete) {
dec_ref(m_type, todelete);
delete(this);
}
expr_app::expr_app(unsigned num_args, bool has_mv): expr_app::expr_app(unsigned num_args, bool has_mv):
expr_cell(expr_kind::App, 0, has_mv), expr_cell(expr_kind::App, 0, has_mv),
@ -196,10 +201,10 @@ void expr_cell::dealloc() {
lean_assert(it->get_rc() == 0); lean_assert(it->get_rc() == 0);
switch (it->kind()) { switch (it->kind()) {
case expr_kind::Var: delete static_cast<expr_var*>(it); break; case expr_kind::Var: delete static_cast<expr_var*>(it); break;
case expr_kind::Constant: delete static_cast<expr_const*>(it); break;
case expr_kind::Value: delete static_cast<expr_value*>(it); break; case expr_kind::Value: delete static_cast<expr_value*>(it); break;
case expr_kind::MetaVar: delete static_cast<expr_metavar*>(it); break; case expr_kind::MetaVar: delete static_cast<expr_metavar*>(it); break;
case expr_kind::Type: delete static_cast<expr_type*>(it); break; case expr_kind::Type: delete static_cast<expr_type*>(it); break;
case expr_kind::Constant: static_cast<expr_const*>(it)->dealloc(todo); break;
case expr_kind::Eq: static_cast<expr_eq*>(it)->dealloc(todo); break; case expr_kind::Eq: static_cast<expr_eq*>(it)->dealloc(todo); break;
case expr_kind::App: static_cast<expr_app*>(it)->dealloc(todo); break; case expr_kind::App: static_cast<expr_app*>(it)->dealloc(todo); break;
case expr_kind::Lambda: static_cast<expr_lambda*>(it)->dealloc(todo); break; case expr_kind::Lambda: static_cast<expr_lambda*>(it)->dealloc(todo); break;
@ -229,7 +234,7 @@ bool is_arrow(expr const & t) {
expr copy(expr const & a) { expr copy(expr const & a) {
switch (a.kind()) { switch (a.kind()) {
case expr_kind::Var: return mk_var(var_idx(a)); case expr_kind::Var: return mk_var(var_idx(a));
case expr_kind::Constant: return mk_constant(const_name(a)); case expr_kind::Constant: return mk_constant(const_name(a), const_type(a));
case expr_kind::Type: return mk_type(ty_level(a)); case expr_kind::Type: return mk_type(ty_level(a));
case expr_kind::Value: return mk_value(static_cast<expr_value*>(a.raw())->m_val); case expr_kind::Value: return mk_value(static_cast<expr_value*>(a.raw())->m_val);
case expr_kind::App: return mk_app(num_args(a), begin_args(a)); case expr_kind::App: return mk_app(num_args(a), begin_args(a));

View file

@ -125,7 +125,7 @@ public:
operator bool() const { return m_ptr != nullptr; } operator bool() const { return m_ptr != nullptr; }
friend expr mk_var(unsigned idx); friend expr mk_var(unsigned idx);
friend expr mk_constant(name const & n); friend expr mk_constant(name const & n, expr const & t);
friend expr mk_value(value & v); friend expr mk_value(value & v);
friend expr mk_app(unsigned num_args, expr const * args); friend expr mk_app(unsigned num_args, expr const * args);
friend expr mk_eq(expr const & l, expr const & r); friend expr mk_eq(expr const & l, expr const & r);
@ -160,9 +160,15 @@ public:
/** \brief Constants. */ /** \brief Constants. */
class expr_const : public expr_cell { class expr_const : public expr_cell {
name m_name; name m_name;
expr m_type; // (optional) cached type
// Remark: we do *not* perform destructive updates on m_type
// This field is used to efficiently implement the tactic framework
friend class expr_cell;
void dealloc(buffer<expr_cell*> & to_delete);
public: public:
expr_const(name const & n); expr_const(name const & n, expr const & type);
name const & get_name() const { return m_name; } name const & get_name() const { return m_name; }
expr const & get_type() const { return m_type; }
}; };
/** \brief Function Applications */ /** \brief Function Applications */
class expr_app : public expr_cell { class expr_app : public expr_cell {
@ -375,7 +381,7 @@ inline bool is_abstraction(expr const & e) { return is_lambda(e) || is_pi(e); }
// Constructors // Constructors
inline expr mk_var(unsigned idx) { return expr(new expr_var(idx)); } inline expr mk_var(unsigned idx) { return expr(new expr_var(idx)); }
inline expr Var(unsigned idx) { return mk_var(idx); } inline expr Var(unsigned idx) { return mk_var(idx); }
inline expr mk_constant(name const & n) { return expr(new expr_const(n)); } inline expr mk_constant(name const & n, expr const & t = expr()) { return expr(new expr_const(n, t)); }
inline expr Const(name const & n) { return mk_constant(n); } inline expr Const(name const & n) { return mk_constant(n); }
inline expr mk_value(value & v) { return expr(new expr_value(v)); } inline expr mk_value(value & v) { return expr(new expr_value(v)); }
inline expr to_expr(value & v) { return mk_value(v); } inline expr to_expr(value & v) { return mk_value(v); }
@ -444,6 +450,8 @@ inline bool is_shared(expr_cell * e) { return get_rc(e) > 1;
inline unsigned var_idx(expr_cell * e) { return to_var(e)->get_vidx(); } inline unsigned var_idx(expr_cell * e) { return to_var(e)->get_vidx(); }
inline bool is_var(expr_cell * e, unsigned i) { return is_var(e) && var_idx(e) == i; } inline bool is_var(expr_cell * e, unsigned i) { return is_var(e) && var_idx(e) == i; }
inline name const & const_name(expr_cell * e) { return to_constant(e)->get_name(); } inline name const & const_name(expr_cell * e) { return to_constant(e)->get_name(); }
// Remark: the following function should not be exposed in the internal API.
inline expr const & const_type(expr_cell * e) { return to_constant(e)->get_type(); }
inline value const & to_value(expr_cell * e) { lean_assert(is_value(e)); return static_cast<expr_value*>(e)->get_value(); } inline value const & to_value(expr_cell * e) { lean_assert(is_value(e)); return static_cast<expr_value*>(e)->get_value(); }
inline unsigned num_args(expr_cell * e) { return to_app(e)->get_num_args(); } inline unsigned num_args(expr_cell * e) { return to_app(e)->get_num_args(); }
inline expr const & arg(expr_cell * e, unsigned idx) { return to_app(e)->get_arg(idx); } inline expr const & arg(expr_cell * e, unsigned idx) { return to_app(e)->get_arg(idx); }
@ -469,6 +477,8 @@ inline unsigned var_idx(expr const & e) { return to_var(e)->ge
/** \brief Return true iff the given expression is a variable with de Bruijn index equal to \c i. */ /** \brief Return true iff the given expression is a variable with de Bruijn index equal to \c i. */
inline bool is_var(expr const & e, unsigned i) { return is_var(e) && var_idx(e) == i; } inline bool is_var(expr const & e, unsigned i) { return is_var(e) && var_idx(e) == i; }
inline name const & const_name(expr const & e) { return to_constant(e)->get_name(); } inline name const & const_name(expr const & e) { return to_constant(e)->get_name(); }
// Remark: the following function should not be exposed in the internal API.
inline expr const & const_type(expr const & e) { return to_constant(e)->get_type(); }
/** \brief Return true iff the given expression is a constant with name \c n. */ /** \brief Return true iff the given expression is a constant with name \c n. */
inline bool is_constant(expr const & e, name const & n) { inline bool is_constant(expr const & e, name const & n) {
return is_constant(e) && const_name(e) == n; return is_constant(e) && const_name(e) == n;
@ -647,6 +657,12 @@ inline expr update_metavar(expr const & e, local_context const & lctx) {
else else
return e; return e;
} }
inline expr update_const(expr const & e, expr const & t) {
if (!is_eqp(const_type(e), t))
return mk_constant(const_name(e), t);
else
return e;
}
// ======================================= // =======================================
std::ostream & operator<<(std::ostream & out, expr const & e); std::ostream & operator<<(std::ostream & out, expr const & e);

View file

@ -46,7 +46,7 @@ class expr_eq_fn {
} }
switch (a.kind()) { switch (a.kind()) {
case expr_kind::Var: lean_unreachable(); // LCOV_EXCL_LINE case expr_kind::Var: lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::Constant: return const_name(a) == const_name(b); case expr_kind::Constant: return const_name(a) == const_name(b) && apply(const_type(a), const_type(b));
case expr_kind::App: case expr_kind::App:
if (num_args(a) != num_args(b)) if (num_args(a) != num_args(b))
return false; return false;

View file

@ -40,7 +40,14 @@ class for_each_fn {
unsigned offset = p.second; unsigned offset = p.second;
if (!CacheAtomic) { if (!CacheAtomic) {
switch (e.kind()) { switch (e.kind()) {
case expr_kind::Constant: case expr_kind::Type: case expr_kind::Value: case expr_kind::Constant:
if (!const_type(e)) {
// only constants without cached types are considered atomic
m_f(e, offset);
goto begin_loop;
}
break;
case expr_kind::Type: case expr_kind::Value:
case expr_kind::Var: case expr_kind::MetaVar: case expr_kind::Var: case expr_kind::MetaVar:
m_f(e, offset); m_f(e, offset);
goto begin_loop; goto begin_loop;
@ -62,7 +69,11 @@ class for_each_fn {
goto begin_loop; goto begin_loop;
switch (e.kind()) { switch (e.kind()) {
case expr_kind::Constant: case expr_kind::Type: case expr_kind::Value: case expr_kind::Constant:
if (const_type(e))
todo.emplace_back(const_type(e), offset);
goto begin_loop;
case expr_kind::Type: case expr_kind::Value:
case expr_kind::Var: case expr_kind::MetaVar: case expr_kind::Var: case expr_kind::MetaVar:
goto begin_loop; goto begin_loop;
case expr_kind::App: { case expr_kind::App: {

View file

@ -24,7 +24,11 @@ protected:
bool apply(expr const & e, unsigned offset) { bool apply(expr const & e, unsigned offset) {
// handle easy cases // handle easy cases
switch (e.kind()) { switch (e.kind()) {
case expr_kind::Constant: case expr_kind::Type: case expr_kind::Value: case expr_kind::Constant:
if (!const_type(e))
return false;
break;
case expr_kind::Type: case expr_kind::Value:
return false; return false;
case expr_kind::MetaVar: case expr_kind::MetaVar:
return true; return true;
@ -58,7 +62,11 @@ protected:
bool result = false; bool result = false;
switch (e.kind()) { switch (e.kind()) {
case expr_kind::Constant: case expr_kind::Type: case expr_kind::Value: case expr_kind::Var: case expr_kind::MetaVar: case expr_kind::Constant:
lean_assert(const_type(e));
result = apply(const_type(e), offset);
break;
case expr_kind::Type: case expr_kind::Value: case expr_kind::Var: case expr_kind::MetaVar:
// easy cases were already handled // easy cases were already handled
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::App: case expr_kind::App:
@ -109,7 +117,11 @@ protected:
bool apply(expr const & e, unsigned offset) { bool apply(expr const & e, unsigned offset) {
// handle easy cases // handle easy cases
switch (e.kind()) { switch (e.kind()) {
case expr_kind::Constant: case expr_kind::Type: case expr_kind::Value: case expr_kind::Constant:
if (!const_type(e))
return false;
break;
case expr_kind::Type: case expr_kind::Value:
return false; return false;
case expr_kind::MetaVar: case expr_kind::MetaVar:
return true; return true;
@ -133,7 +145,11 @@ protected:
bool result = false; bool result = false;
switch (e.kind()) { switch (e.kind()) {
case expr_kind::Constant: case expr_kind::Type: case expr_kind::Value: case expr_kind::Var: case expr_kind::MetaVar: case expr_kind::Constant:
lean_assert(const_type(e));
result = apply(const_type(e), offset);
break;
case expr_kind::Type: case expr_kind::Value: case expr_kind::Var: case expr_kind::MetaVar:
// easy cases were already handled // easy cases were already handled
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::App: case expr_kind::App:

View file

@ -0,0 +1,27 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include "kernel/expr.h"
#include "kernel/for_each.h"
namespace lean {
bool has_cached_type(expr const & e) {
bool result = false;
auto f = [&](expr const & e, unsigned) {
if (result) {
return false; // do not continue the search
} else if (is_constant(e) && const_type(e)) {
result = true;
return false;
} else {
return true;
}
};
for_each_fn<decltype(f)> proc(f);
proc(e);
return result;
}
}

View file

@ -0,0 +1,22 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include "kernel/expr.h"
namespace lean {
/**
\brief Return true iff \c e contains a constant with a cached type.
The kernel should *not* accept expressions containing cached types.
Reason: Cached types may introduce unsoundness.
For example, in the environment env, the constant x may have type T.
Now suppose we are trying to add a new definition D that contains x,
and x is associated with a cached type T'. The cached type may allow
us to accept a definition that is type incorrect with respect to env.
*/
bool has_cached_type(expr const & e);
}

View file

@ -59,7 +59,11 @@ class replace_fn {
expr r = m_f(e, offset); expr r = m_f(e, offset);
if (is_eqp(e, r)) { if (is_eqp(e, r)) {
switch (e.kind()) { switch (e.kind()) {
case expr_kind::Type: case expr_kind::Value: case expr_kind::Constant: case expr_kind::Constant:
if (const_type(e))
r = update_const(e, apply(const_type(e), offset));
break;
case expr_kind::Type: case expr_kind::Value:
case expr_kind::Var: case expr_kind::MetaVar: case expr_kind::Var: case expr_kind::MetaVar:
break; break;
case expr_kind::App: case expr_kind::App:

View file

@ -10,9 +10,16 @@ Author: Leonardo de Moura
namespace lean { namespace lean {
expr replace_visitor::visit_type(expr const & e, context const &) { lean_assert(is_type(e)); return e; } expr replace_visitor::visit_type(expr const & e, context const &) { lean_assert(is_type(e)); return e; }
expr replace_visitor::visit_value(expr const & e, context const &) { lean_assert(is_value(e)); return e; } expr replace_visitor::visit_value(expr const & e, context const &) { lean_assert(is_value(e)); return e; }
expr replace_visitor::visit_constant(expr const & e, context const &) { lean_assert(is_constant(e)); return e; }
expr replace_visitor::visit_var(expr const & e, context const &) { lean_assert(is_var(e)); return e; } expr replace_visitor::visit_var(expr const & e, context const &) { lean_assert(is_var(e)); return e; }
expr replace_visitor::visit_metavar(expr const & e, context const &) { lean_assert(is_metavar(e)); return e; } expr replace_visitor::visit_metavar(expr const & e, context const &) { lean_assert(is_metavar(e)); return e; }
expr replace_visitor::visit_constant(expr const & e, context const & ctx) {
lean_assert(is_constant(e));
if (const_type(e)) {
return update_const(e, visit(const_type(e), ctx));
} else {
return e;
}
}
expr replace_visitor::visit_app(expr const & e, context const & ctx) { expr replace_visitor::visit_app(expr const & e, context const & ctx) {
lean_assert(is_app(e)); lean_assert(is_app(e));
return update_app(e, [&](expr const & c) { return visit(c, ctx); }); return update_app(e, [&](expr const & c) { return visit(c, ctx); });

View file

@ -107,11 +107,15 @@ class type_checker::imp {
} }
break; break;
case expr_kind::Constant: { case expr_kind::Constant: {
object const & obj = env().get_object(const_name(e)); if (const_type(e)) {
if (obj.has_type()) r = const_type(e);
r = obj.get_type(); } else {
else object const & obj = env().get_object(const_name(e));
throw has_no_type_exception(env(), e); if (obj.has_type())
r = obj.get_type();
else
throw has_no_type_exception(env(), e);
}
break; break;
} }
case expr_kind::Var: case expr_kind::Var:

View file

@ -36,7 +36,16 @@ struct max_sharing_fn::imp {
return a; return a;
} }
switch (a.kind()) { switch (a.kind()) {
case expr_kind::Var: case expr_kind::Constant: case expr_kind::Type: case expr_kind::Value: case expr_kind::Constant:
if (const_type(a)) {
expr r = update_const(a, apply(const_type(a)));
cache(r);
return r;
} else {
cache(a);
return a;
}
case expr_kind::Var: case expr_kind::Type: case expr_kind::Value:
cache(a); cache(a);
return a; return a;
case expr_kind::App: { case expr_kind::App: {

View file

@ -110,11 +110,15 @@ class type_inferer::imp {
throw unexpected_metavar_occurrence(m_env, e); throw unexpected_metavar_occurrence(m_env, e);
} }
case expr_kind::Constant: { case expr_kind::Constant: {
object const & obj = m_env.get_object(const_name(e)); if (const_type(e)) {
if (obj.has_type()) return const_type(e);
return obj.get_type(); } else {
else object const & obj = m_env.get_object(const_name(e));
throw has_no_type_exception(m_env, e); if (obj.has_type())
return obj.get_type();
else
throw has_no_type_exception(m_env, e);
}
break; break;
} }
case expr_kind::Var: { case expr_kind::Var: {