fix(kernel/environment): add weak reference to environment objects
We need weak references to environment objects because the environment has a reference to the type_checker and the type_checker has a reference back to the environment. Before, we were breaking the cycle using an "environment const &". This was a dangerous hack because the environment smart pointer passed to the type_checker could be on the stack. The weak_ref is much safer. Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
9c60eed93c
commit
8012c4f644
6 changed files with 75 additions and 47 deletions
|
@ -352,7 +352,11 @@ struct lean_extension_initializer {
|
||||||
|
|
||||||
static lean_extension_initializer g_lean_extension_initializer;
|
static lean_extension_initializer g_lean_extension_initializer;
|
||||||
|
|
||||||
static lean_extension & to_ext(environment const & env) {
|
static lean_extension const & to_ext(environment const & env) {
|
||||||
|
return env.get_extension<lean_extension>(g_lean_extension_initializer.m_extid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static lean_extension & to_ext(environment & env) {
|
||||||
return env.get_extension<lean_extension>(g_lean_extension_initializer.m_extid);
|
return env.get_extension<lean_extension>(g_lean_extension_initializer.m_extid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@ public:
|
||||||
|
|
||||||
environment const & get_environment() const { return m_env; }
|
environment const & get_environment() const { return m_env; }
|
||||||
operator environment const &() const { return get_environment(); }
|
operator environment const &() const { return get_environment(); }
|
||||||
|
environment & get_environment() { return m_env; }
|
||||||
|
operator environment &() { return get_environment(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@name Environment API
|
@name Environment API
|
||||||
|
@ -179,6 +181,8 @@ public:
|
||||||
/*@{*/
|
/*@{*/
|
||||||
state const & get_state() const { return m_state; }
|
state const & get_state() const { return m_state; }
|
||||||
operator state const &() const { return m_state; }
|
operator state const &() const { return m_state; }
|
||||||
|
state & get_state() { return m_state; }
|
||||||
|
operator state &() { return m_state; }
|
||||||
options get_options() const { return m_state.get_options(); }
|
options get_options() const { return m_state.get_options(); }
|
||||||
void set_options(options const & opts) { return m_state.set_options(opts); }
|
void set_options(options const & opts) { return m_state.set_options(opts); }
|
||||||
template<typename T> void set_option(name const & n, T const & v) { m_state.set_option(n, v); }
|
template<typename T> void set_option(name const & n, T const & v) { m_state.set_option(n, v); }
|
||||||
|
|
|
@ -65,14 +65,12 @@ struct environment::imp {
|
||||||
// Object management
|
// Object management
|
||||||
std::vector<object> m_objects;
|
std::vector<object> m_objects;
|
||||||
object_dictionary m_object_dictionary;
|
object_dictionary m_object_dictionary;
|
||||||
type_checker m_type_checker;
|
std::unique_ptr<type_checker> m_type_checker;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<extension>> m_extensions;
|
std::vector<std::unique_ptr<extension>> m_extensions;
|
||||||
friend class extension;
|
friend class extension;
|
||||||
|
|
||||||
extension & get_extension_core(unsigned extid, environment const & env) {
|
extension & get_extension_core(unsigned extid) {
|
||||||
if (has_children())
|
|
||||||
throw read_only_environment_exception(env);
|
|
||||||
if (extid >= m_extensions.size())
|
if (extid >= m_extensions.size())
|
||||||
m_extensions.resize(extid+1);
|
m_extensions.resize(extid+1);
|
||||||
if (!m_extensions[extid]) {
|
if (!m_extensions[extid]) {
|
||||||
|
@ -288,9 +286,9 @@ struct environment::imp {
|
||||||
infer_universe and infer_type expect an environment instead of environment::imp.
|
infer_universe and infer_type expect an environment instead of environment::imp.
|
||||||
*/
|
*/
|
||||||
void check_type(name const & n, expr const & t, expr const & v, environment const & env) {
|
void check_type(name const & n, expr const & t, expr const & v, environment const & env) {
|
||||||
m_type_checker.check_type(t);
|
m_type_checker->check_type(t);
|
||||||
expr v_t = m_type_checker.infer_type(v);
|
expr v_t = m_type_checker->infer_type(v);
|
||||||
if (!m_type_checker.is_convertible(v_t, t))
|
if (!m_type_checker->is_convertible(v_t, t))
|
||||||
throw def_type_mismatch_exception(env, n, t, v, v_t);
|
throw def_type_mismatch_exception(env, n, t, v, v_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +333,7 @@ struct environment::imp {
|
||||||
*/
|
*/
|
||||||
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_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;
|
||||||
register_named_object(mk_definition(n, v_t, v, opaque, w));
|
register_named_object(mk_definition(n, v_t, v, opaque, w));
|
||||||
}
|
}
|
||||||
|
@ -349,14 +347,14 @@ struct environment::imp {
|
||||||
/** \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_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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \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_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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,11 +379,11 @@ struct environment::imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
expr infer_type(expr const & e, context const & ctx) {
|
expr infer_type(expr const & e, context const & ctx) {
|
||||||
return m_type_checker.infer_type(e, ctx);
|
return m_type_checker->infer_type(e, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
expr normalize(expr const & e, context const & ctx) {
|
expr normalize(expr const & e, context const & ctx) {
|
||||||
return m_type_checker.get_normalizer()(e, ctx);
|
return m_type_checker->get_normalizer()(e, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \brief Display universal variable constraints and objects stored in this environment and its parents. */
|
/** \brief Display universal variable constraints and objects stored in this environment and its parents. */
|
||||||
|
@ -400,19 +398,17 @@ struct environment::imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_interrupt(bool flag) {
|
void set_interrupt(bool flag) {
|
||||||
m_type_checker.set_interrupt(flag);
|
m_type_checker->set_interrupt(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
imp(environment const & env):
|
imp():
|
||||||
m_num_children(0),
|
m_num_children(0) {
|
||||||
m_type_checker(env) {
|
|
||||||
init_uvars();
|
init_uvars();
|
||||||
}
|
}
|
||||||
|
|
||||||
imp(std::shared_ptr<imp> const & parent, environment const & env):
|
imp(std::shared_ptr<imp> const & parent):
|
||||||
m_num_children(0),
|
m_num_children(0),
|
||||||
m_parent(parent),
|
m_parent(parent) {
|
||||||
m_type_checker(env) {
|
|
||||||
m_parent->inc_children();
|
m_parent->inc_children();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,12 +419,14 @@ struct environment::imp {
|
||||||
};
|
};
|
||||||
|
|
||||||
environment::environment():
|
environment::environment():
|
||||||
m_ptr(new imp(*this)) {
|
m_ptr(new imp()) {
|
||||||
|
m_ptr->m_type_checker.reset(new type_checker(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// used when creating a new child environment
|
// used when creating a new child environment
|
||||||
environment::environment(std::shared_ptr<imp> const & parent, bool):
|
environment::environment(std::shared_ptr<imp> const & parent, bool):
|
||||||
m_ptr(new imp(parent, *this)) {
|
m_ptr(new imp(parent)) {
|
||||||
|
m_ptr->m_type_checker.reset(new type_checker(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// used when creating a reference to the parent environment
|
// used when creating a reference to the parent environment
|
||||||
|
@ -532,8 +530,12 @@ void environment::set_interrupt(bool flag) {
|
||||||
m_ptr->set_interrupt(flag);
|
m_ptr->set_interrupt(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
environment::extension & environment::get_extension_core(unsigned extid) const {
|
environment::extension const & environment::get_extension_core(unsigned extid) const {
|
||||||
return m_ptr->get_extension_core(extid, *this);
|
return m_ptr->get_extension_core(extid);
|
||||||
|
}
|
||||||
|
|
||||||
|
environment::extension & environment::get_extension_core(unsigned extid) {
|
||||||
|
return m_ptr->get_extension_core(extid);
|
||||||
}
|
}
|
||||||
|
|
||||||
environment::extension::extension():
|
environment::extension::extension():
|
||||||
|
|
|
@ -23,7 +23,6 @@ private:
|
||||||
void check_type(name const & n, expr const & t, expr const & v);
|
void check_type(name const & n, expr const & t, expr const & v);
|
||||||
environment(std::shared_ptr<imp> const & parent, bool);
|
environment(std::shared_ptr<imp> const & parent, bool);
|
||||||
explicit environment(std::shared_ptr<imp> const & ptr);
|
explicit environment(std::shared_ptr<imp> const & ptr);
|
||||||
explicit environment(imp * new_ptr);
|
|
||||||
unsigned get_num_objects(bool local) const;
|
unsigned get_num_objects(bool local) const;
|
||||||
object const & get_object(unsigned i, bool local) const;
|
object const & get_object(unsigned i, bool local) const;
|
||||||
public:
|
public:
|
||||||
|
@ -253,7 +252,8 @@ public:
|
||||||
static unsigned register_extension(mk_extension mk);
|
static unsigned register_extension(mk_extension mk);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
extension & get_extension_core(unsigned extid) const;
|
extension const & get_extension_core(unsigned extid) const;
|
||||||
|
extension & get_extension_core(unsigned extid);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -261,10 +261,22 @@ public:
|
||||||
The token is the value returned by \c register_extension.
|
The token is the value returned by \c register_extension.
|
||||||
*/
|
*/
|
||||||
template<typename Ext>
|
template<typename Ext>
|
||||||
Ext & get_extension(unsigned extid) const {
|
Ext const & get_extension(unsigned extid) const {
|
||||||
|
extension const & ext = get_extension_core(extid);
|
||||||
|
lean_assert(dynamic_cast<Ext const *>(&ext) != nullptr);
|
||||||
|
return static_cast<Ext const &>(ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Ext>
|
||||||
|
Ext & get_extension(unsigned extid) {
|
||||||
extension & ext = get_extension_core(extid);
|
extension & ext = get_extension_core(extid);
|
||||||
lean_assert(dynamic_cast<Ext*>(&ext) != nullptr);
|
lean_assert(dynamic_cast<Ext*>(&ext) != nullptr);
|
||||||
return static_cast<Ext&>(ext);
|
return static_cast<Ext&>(ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef std::weak_ptr<imp> weak_ref;
|
||||||
|
weak_ref to_weak_ref() const { return weak_ref(m_ptr); }
|
||||||
|
environment(weak_ref const & r):m_ptr(r) { lean_assert(!r.expired()); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,13 +69,15 @@ value_stack extend(value_stack const & s, svalue const & v) { return cons(v, s);
|
||||||
class normalizer::imp {
|
class normalizer::imp {
|
||||||
typedef scoped_map<expr, svalue, expr_hash, expr_eqp> cache;
|
typedef scoped_map<expr, svalue, expr_hash, expr_eqp> cache;
|
||||||
|
|
||||||
environment const & m_env;
|
environment::weak_ref m_env;
|
||||||
context m_ctx;
|
context m_ctx;
|
||||||
cache m_cache;
|
cache m_cache;
|
||||||
unsigned m_max_depth;
|
unsigned m_max_depth;
|
||||||
unsigned m_depth;
|
unsigned m_depth;
|
||||||
volatile bool m_interrupted;
|
volatile bool m_interrupted;
|
||||||
|
|
||||||
|
environment env() const { return environment(m_env); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Auxiliary object for saving the current context.
|
\brief Auxiliary object for saving the current context.
|
||||||
We need this to be able to process values in the context.
|
We need this to be able to process values in the context.
|
||||||
|
@ -179,7 +181,7 @@ class normalizer::imp {
|
||||||
flet<unsigned> l(m_depth, m_depth+1);
|
flet<unsigned> l(m_depth, m_depth+1);
|
||||||
check_interrupted(m_interrupted);
|
check_interrupted(m_interrupted);
|
||||||
if (m_depth > m_max_depth)
|
if (m_depth > m_max_depth)
|
||||||
throw kernel_exception(m_env, "normalizer maximum recursion depth exceeded");
|
throw kernel_exception(env(), "normalizer maximum recursion depth exceeded");
|
||||||
bool shared = false;
|
bool shared = false;
|
||||||
if (is_shared(a)) {
|
if (is_shared(a)) {
|
||||||
shared = true;
|
shared = true;
|
||||||
|
@ -197,7 +199,7 @@ class normalizer::imp {
|
||||||
r = lookup(s, var_idx(a));
|
r = lookup(s, var_idx(a));
|
||||||
break;
|
break;
|
||||||
case expr_kind::Constant: {
|
case expr_kind::Constant: {
|
||||||
object const & obj = m_env.get_object(const_name(a));
|
object const & obj = env().get_object(const_name(a));
|
||||||
if (obj.is_definition() && !obj.is_opaque()) {
|
if (obj.is_definition() && !obj.is_opaque()) {
|
||||||
r = normalize(obj.get_value(), value_stack(), 0);
|
r = normalize(obj.get_value(), value_stack(), 0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -291,7 +293,7 @@ class normalizer::imp {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
imp(environment const & env, unsigned max_depth):
|
imp(environment const & env, unsigned max_depth):
|
||||||
m_env(env) {
|
m_env(env.to_weak_ref()) {
|
||||||
m_interrupted = false;
|
m_interrupted = false;
|
||||||
m_max_depth = max_depth;
|
m_max_depth = max_depth;
|
||||||
m_depth = 0;
|
m_depth = 0;
|
||||||
|
|
|
@ -22,7 +22,7 @@ class type_checker::imp {
|
||||||
typedef scoped_map<expr, expr, expr_hash, expr_eqp> cache;
|
typedef scoped_map<expr, expr, expr_hash, expr_eqp> cache;
|
||||||
typedef buffer<unification_constraint> unification_constraints;
|
typedef buffer<unification_constraint> unification_constraints;
|
||||||
|
|
||||||
environment const & m_env;
|
environment::weak_ref m_env;
|
||||||
cache m_cache;
|
cache m_cache;
|
||||||
normalizer m_normalizer;
|
normalizer m_normalizer;
|
||||||
context m_ctx;
|
context m_ctx;
|
||||||
|
@ -31,6 +31,10 @@ class type_checker::imp {
|
||||||
unification_constraints * m_uc;
|
unification_constraints * m_uc;
|
||||||
volatile bool m_interrupted;
|
volatile bool m_interrupted;
|
||||||
|
|
||||||
|
environment env() const {
|
||||||
|
return environment(m_env);
|
||||||
|
}
|
||||||
|
|
||||||
expr normalize(expr const & e, context const & ctx) {
|
expr normalize(expr const & e, context const & ctx) {
|
||||||
return m_normalizer(e, ctx);
|
return m_normalizer(e, ctx);
|
||||||
}
|
}
|
||||||
|
@ -59,7 +63,7 @@ class type_checker::imp {
|
||||||
m_uc->push_back(mk_eq_constraint(ctx, r, p, jst));
|
m_uc->push_back(mk_eq_constraint(ctx, r, p, jst));
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
throw function_expected_exception(m_env, ctx, s);
|
throw function_expected_exception(env(), ctx, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
expr check_type(expr const & e, expr const & s, context const & ctx) {
|
expr check_type(expr const & e, expr const & s, context const & ctx) {
|
||||||
|
@ -77,7 +81,7 @@ class type_checker::imp {
|
||||||
m_uc->push_back(mk_convertible_constraint(ctx, u, TypeU, jst));
|
m_uc->push_back(mk_convertible_constraint(ctx, u, TypeU, jst));
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
throw type_expected_exception(m_env, ctx, s);
|
throw type_expected_exception(env(), ctx, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
expr infer_type_core(expr const & e, context const & ctx) {
|
expr infer_type_core(expr const & e, context const & ctx) {
|
||||||
|
@ -99,11 +103,11 @@ class type_checker::imp {
|
||||||
else
|
else
|
||||||
return m_menv->get_type(e);
|
return m_menv->get_type(e);
|
||||||
} else {
|
} else {
|
||||||
throw unexpected_metavar_occurrence(m_env, e);
|
throw unexpected_metavar_occurrence(env(), e);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case expr_kind::Constant:
|
case expr_kind::Constant:
|
||||||
r = m_env.get_object(const_name(e)).get_type();
|
r = env().get_object(const_name(e)).get_type();
|
||||||
break;
|
break;
|
||||||
case expr_kind::Var:
|
case expr_kind::Var:
|
||||||
r = lookup(ctx, var_idx(e));
|
r = lookup(ctx, var_idx(e));
|
||||||
|
@ -125,7 +129,7 @@ class type_checker::imp {
|
||||||
expr const & c_t = arg_types[i];
|
expr const & c_t = arg_types[i];
|
||||||
auto mk_justification = [&](){ return mk_app_type_match_justification(ctx, e, i); }; // thunk for creating justification object if needed
|
auto mk_justification = [&](){ return mk_app_type_match_justification(ctx, e, i); }; // thunk for creating justification object if needed
|
||||||
if (!is_convertible(c_t, abst_domain(f_t), ctx, mk_justification))
|
if (!is_convertible(c_t, abst_domain(f_t), ctx, mk_justification))
|
||||||
throw app_type_mismatch_exception(m_env, ctx, e, arg_types.size(), arg_types.data());
|
throw app_type_mismatch_exception(env(), ctx, e, arg_types.size(), arg_types.data());
|
||||||
if (closed(abst_body(f_t)))
|
if (closed(abst_body(f_t)))
|
||||||
f_t = abst_body(f_t);
|
f_t = abst_body(f_t);
|
||||||
else if (closed(c))
|
else if (closed(c))
|
||||||
|
@ -182,7 +186,7 @@ class type_checker::imp {
|
||||||
check_type(ty, let_type(e), ctx); // check if it is really a type
|
check_type(ty, let_type(e), ctx); // check if it is really a type
|
||||||
auto mk_justification = [&](){ return mk_def_type_match_justification(ctx, let_name(e), let_value(e)); }; // thunk for creating justification object if needed
|
auto mk_justification = [&](){ return mk_def_type_match_justification(ctx, let_name(e), let_value(e)); }; // thunk for creating justification object if needed
|
||||||
if (!is_convertible(lt, let_type(e), ctx, mk_justification))
|
if (!is_convertible(lt, let_type(e), ctx, mk_justification))
|
||||||
throw def_type_mismatch_exception(m_env, ctx, let_name(e), let_type(e), let_value(e), lt);
|
throw def_type_mismatch_exception(env(), ctx, let_name(e), let_type(e), let_value(e), lt);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
cache::mk_scope sc(m_cache);
|
cache::mk_scope sc(m_cache);
|
||||||
|
@ -194,11 +198,11 @@ class type_checker::imp {
|
||||||
case expr_kind::Value: {
|
case expr_kind::Value: {
|
||||||
// Check if the builtin value (or its set) is declared in the environment.
|
// Check if the builtin value (or its set) is declared in the environment.
|
||||||
name const & n = to_value(e).get_name();
|
name const & n = to_value(e).get_name();
|
||||||
object const & obj = m_env.get_object(n);
|
object const & obj = env().get_object(n);
|
||||||
if (obj && ((obj.is_builtin() && obj.get_value() == e) || (obj.is_builtin_set() && obj.in_builtin_set(e)))) {
|
if (obj && ((obj.is_builtin() && obj.get_value() == e) || (obj.is_builtin_set() && obj.in_builtin_set(e)))) {
|
||||||
r = to_value(e).get_type();
|
r = to_value(e).get_type();
|
||||||
} else {
|
} else {
|
||||||
throw invalid_builtin_value_reference(m_env, e);
|
throw invalid_builtin_value_reference(env(), e);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -218,7 +222,7 @@ class type_checker::imp {
|
||||||
expr const * e = &expected;
|
expr const * e = &expected;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (is_type(*e) && is_type(*g)) {
|
if (is_type(*e) && is_type(*g)) {
|
||||||
if (m_env.is_ge(ty_level(*e), ty_level(*g)))
|
if (env().is_ge(ty_level(*e), ty_level(*g)))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +279,7 @@ class type_checker::imp {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
imp(environment const & env):
|
imp(environment const & env):
|
||||||
m_env(env),
|
m_env(env.to_weak_ref()),
|
||||||
m_normalizer(env) {
|
m_normalizer(env) {
|
||||||
m_menv = nullptr;
|
m_menv = nullptr;
|
||||||
m_menv_timestamp = 0;
|
m_menv_timestamp = 0;
|
||||||
|
|
Loading…
Reference in a new issue