2013-07-29 05:34:39 +00:00
|
|
|
/*
|
|
|
|
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 <algorithm>
|
|
|
|
#include <vector>
|
2013-08-04 23:07:37 +00:00
|
|
|
#include <atomic>
|
2013-08-05 00:47:54 +00:00
|
|
|
#include <sstream>
|
2013-08-05 03:52:14 +00:00
|
|
|
#include <unordered_map>
|
2013-07-29 05:34:39 +00:00
|
|
|
#include "environment.h"
|
2013-08-05 22:17:58 +00:00
|
|
|
#include "safe_arith.h"
|
2013-08-05 03:52:14 +00:00
|
|
|
#include "type_check.h"
|
2013-07-29 05:34:39 +00:00
|
|
|
#include "exception.h"
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
namespace lean {
|
|
|
|
constexpr unsigned uninit = std::numeric_limits<int>::max();
|
|
|
|
|
2013-08-05 03:52:14 +00:00
|
|
|
environment::definition::definition(name const & n, expr const & t, expr const & v, bool opaque):
|
|
|
|
m_name(n),
|
|
|
|
m_type(t),
|
|
|
|
m_value(v),
|
|
|
|
m_opaque(opaque) {
|
|
|
|
}
|
|
|
|
|
|
|
|
environment::definition::~definition() {
|
|
|
|
}
|
|
|
|
|
|
|
|
environment::object_kind environment::definition::kind() const {
|
|
|
|
return object_kind::Definition;
|
|
|
|
}
|
|
|
|
|
|
|
|
void environment::definition::display(std::ostream & out) const {
|
|
|
|
out << "Definition " << m_name << " : " << m_type << " := " << m_value << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
environment::fact::fact(name const & n, expr const & t):
|
|
|
|
m_name(n),
|
|
|
|
m_type(t) {
|
|
|
|
}
|
|
|
|
|
|
|
|
environment::fact::~fact() {
|
|
|
|
}
|
|
|
|
|
|
|
|
environment::object_kind environment::fact::kind() const {
|
|
|
|
return object_kind::Fact;
|
|
|
|
}
|
|
|
|
|
|
|
|
void environment::fact::display(std::ostream & out) const {
|
|
|
|
out << "Fact " << m_name << " : " << m_type << "\n";
|
|
|
|
}
|
|
|
|
|
2013-07-30 02:44:26 +00:00
|
|
|
/** \brief Implementation of the Lean environment. */
|
2013-07-29 05:34:39 +00:00
|
|
|
struct environment::imp {
|
2013-08-05 03:52:14 +00:00
|
|
|
typedef std::unordered_map<name, object *, name_hash, name_eq> object_dictionary;
|
2013-08-05 22:17:58 +00:00
|
|
|
typedef std::tuple<level, level, int> constraint;
|
|
|
|
// Universe variable management
|
|
|
|
std::vector<constraint> m_constraints;
|
2013-08-05 03:52:14 +00:00
|
|
|
std::vector<level> m_uvars;
|
2013-08-05 22:17:58 +00:00
|
|
|
// Children environment management
|
2013-08-05 03:52:14 +00:00
|
|
|
std::atomic<unsigned> m_num_children;
|
|
|
|
std::shared_ptr<imp> m_parent;
|
2013-08-05 22:17:58 +00:00
|
|
|
// Object management
|
2013-08-05 03:52:14 +00:00
|
|
|
std::vector<object*> m_objects;
|
|
|
|
object_dictionary m_object_dictionary;
|
2013-08-04 23:07:37 +00:00
|
|
|
|
|
|
|
bool has_children() const { return m_num_children > 0; }
|
|
|
|
void inc_children() { m_num_children++; }
|
|
|
|
void dec_children() { m_num_children--; }
|
|
|
|
|
|
|
|
bool has_parent() const { return m_parent != nullptr; }
|
2013-07-29 05:34:39 +00:00
|
|
|
|
2013-08-05 22:17:58 +00:00
|
|
|
/** \brief Return true if l1 >= l2 + k is implied by constraints
|
|
|
|
\pre is_uvar(l1) && is_uvar(l2)
|
|
|
|
*/
|
|
|
|
bool is_implied(level const & l1, level const & l2, int k) {
|
|
|
|
lean_assert(is_uvar(l1) && is_uvar(l2));
|
|
|
|
if (l1 == l2)
|
|
|
|
return k <= 0;
|
|
|
|
else
|
|
|
|
return std::any_of(m_constraints.begin(), m_constraints.end(),
|
|
|
|
[&](constraint const & c) { return std::get<0>(c) == l1 && std::get<1>(c) == l2 && std::get<2>(c) >= k; });
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Return true iff l1 >= l2 + k */
|
2013-07-30 02:05:43 +00:00
|
|
|
bool is_ge(level const & l1, level const & l2, int k) {
|
2013-08-05 22:17:58 +00:00
|
|
|
if (l1 == l2)
|
|
|
|
return k == 0;
|
2013-07-29 05:34:39 +00:00
|
|
|
switch (kind(l2)) {
|
|
|
|
case level_kind::UVar:
|
|
|
|
switch (kind(l1)) {
|
2013-08-05 22:17:58 +00:00
|
|
|
case level_kind::UVar: return is_implied(l1, l2, k);
|
|
|
|
case level_kind::Lift: return is_ge(lift_of(l1), l2, safe_sub(k, lift_offset(l1)));
|
2013-08-02 01:52:28 +00:00
|
|
|
case level_kind::Max: return std::any_of(max_begin_levels(l1), max_end_levels(l1), [&](level const & l) { return is_ge(l, l2, k); });
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|
2013-08-05 22:17:58 +00:00
|
|
|
case level_kind::Lift: return is_ge(l1, lift_of(l2), safe_add(k, lift_offset(l2)));
|
2013-08-02 01:52:28 +00:00
|
|
|
case level_kind::Max: return std::all_of(max_begin_levels(l2), max_end_levels(l2), [&](level const & l) { return is_ge(l1, l, k); });
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|
|
|
|
lean_unreachable();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-07-30 02:05:43 +00:00
|
|
|
bool is_ge(level const & l1, level const & l2) {
|
2013-08-05 00:47:54 +00:00
|
|
|
if (has_parent())
|
2013-08-04 23:07:37 +00:00
|
|
|
return m_parent->is_ge(l1, l2);
|
2013-08-05 00:47:54 +00:00
|
|
|
else
|
|
|
|
return is_ge(l1, l2, 0);
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
level add_var(name const & n) {
|
|
|
|
if (std::any_of(m_uvars.begin(), m_uvars.end(), [&](level const & l){ return uvar_name(l) == n; }))
|
|
|
|
throw exception("invalid universe variable declaration, it has already been declared");
|
2013-08-05 22:17:58 +00:00
|
|
|
level r(n);
|
2013-07-29 05:34:39 +00:00
|
|
|
m_uvars.push_back(r);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-08-05 22:17:58 +00:00
|
|
|
void add_constraint(level const & l1, level const & l2, int d) {
|
|
|
|
if (is_implied(l1, l2, d))
|
|
|
|
return; // redundant
|
|
|
|
buffer<constraint> to_add;
|
|
|
|
for (constraint const & c : m_constraints) {
|
|
|
|
if (std::get<0>(c) == l2) {
|
|
|
|
level const & l3 = std::get<1>(c);
|
|
|
|
int l1_l3_d = safe_add(d, std::get<2>(c));
|
|
|
|
if (!is_implied(l1, l3, l1_l3_d))
|
|
|
|
to_add.push_back(constraint(l1, l3, l1_l3_d));
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|
|
|
|
}
|
2013-08-05 22:17:58 +00:00
|
|
|
m_constraints.push_back(constraint(l1, l2, d));
|
|
|
|
for (constraint const & c: to_add) {
|
|
|
|
m_constraints.push_back(c);
|
|
|
|
}
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|
|
|
|
|
2013-08-05 22:17:58 +00:00
|
|
|
void add_constraints(level const & n, level const & l, int k) {
|
|
|
|
lean_assert(is_uvar(n));
|
2013-07-29 05:34:39 +00:00
|
|
|
switch (kind(l)) {
|
2013-08-05 22:17:58 +00:00
|
|
|
case level_kind::UVar: add_constraint(n, l, k); return;
|
|
|
|
case level_kind::Lift: add_constraints(n, lift_of(l), safe_add(k, lift_offset(l))); return;
|
|
|
|
case level_kind::Max: std::for_each(max_begin_levels(l), max_end_levels(l), [&](level const & l1) { add_constraints(n, l1, k); }); return;
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|
|
|
|
lean_unreachable();
|
|
|
|
}
|
|
|
|
|
|
|
|
level define_uvar(name const & n, level const & l) {
|
2013-08-04 23:07:37 +00:00
|
|
|
if (has_parent())
|
|
|
|
throw exception("invalid universe declaration, universe variables can only be declared in top-level environments");
|
|
|
|
if (has_children())
|
|
|
|
throw exception("invalid universe declaration, environment has children environments");
|
2013-07-29 05:34:39 +00:00
|
|
|
level r = add_var(n);
|
2013-08-05 22:17:58 +00:00
|
|
|
add_constraints(r, l, 0);
|
2013-07-29 05:34:39 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-08-05 00:47:54 +00:00
|
|
|
level get_uvar(name const & n) const {
|
|
|
|
if (has_parent()) {
|
|
|
|
return m_parent->get_uvar(n);
|
|
|
|
} else {
|
|
|
|
auto it = std::find_if(m_uvars.begin(), m_uvars.end(), [&](level const & l) { return uvar_name(l) == n; });
|
|
|
|
if (it == m_uvars.end()) {
|
|
|
|
std::ostringstream s;
|
|
|
|
s << "unknown universe variable '" << n << "'";
|
|
|
|
throw exception (s.str());
|
|
|
|
} else {
|
|
|
|
return *it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-29 05:34:39 +00:00
|
|
|
void init_uvars() {
|
|
|
|
m_uvars.push_back(level());
|
|
|
|
}
|
|
|
|
|
|
|
|
void display_uvars(std::ostream & out) const {
|
2013-08-05 22:17:58 +00:00
|
|
|
for (constraint const & c : m_constraints) {
|
|
|
|
out << uvar_name(std::get<0>(c)) << " >= " << uvar_name(std::get<1>(c));
|
|
|
|
if (std::get<2>(c) >= 0)
|
|
|
|
out << " + " << std::get<2>(c);
|
|
|
|
out << "\n";
|
|
|
|
}
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|
|
|
|
|
2013-08-05 03:52:14 +00:00
|
|
|
void check_no_children() {
|
|
|
|
if (has_children())
|
|
|
|
throw exception("invalid object declaration, environment has children environments");
|
|
|
|
}
|
|
|
|
|
|
|
|
void check_name(name const & n) {
|
|
|
|
if (m_object_dictionary.find(n) != m_object_dictionary.end()) {
|
|
|
|
std::ostringstream s;
|
|
|
|
s << "environment already contains an object with name '" << n << "'";
|
|
|
|
throw exception (s.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_definition(name const & n, expr const & t, expr const & v, bool opaque) {
|
|
|
|
m_objects.push_back(new definition(n, t, v, opaque));
|
|
|
|
m_object_dictionary.insert(std::make_pair(n, m_objects.back()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_fact(name const & n, expr const & t) {
|
|
|
|
m_objects.push_back(new fact(n, t));
|
|
|
|
m_object_dictionary.insert(std::make_pair(n, m_objects.back()));
|
|
|
|
}
|
|
|
|
|
|
|
|
object const * get_object_ptr(name const & n) const {
|
|
|
|
auto it = m_object_dictionary.find(n);
|
|
|
|
if (it == m_object_dictionary.end()) {
|
|
|
|
if (has_parent())
|
|
|
|
return m_parent->get_object_ptr(n);
|
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
} else {
|
|
|
|
return it->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
object const & get_object(name const & n) const {
|
|
|
|
object const * ptr = get_object_ptr(n);
|
|
|
|
if (ptr) {
|
|
|
|
return *ptr;
|
|
|
|
} else {
|
|
|
|
std::ostringstream s;
|
|
|
|
s << "unknown object '" << n << "'";
|
|
|
|
throw exception (s.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-04 23:07:37 +00:00
|
|
|
imp():
|
|
|
|
m_num_children(0) {
|
2013-07-29 05:34:39 +00:00
|
|
|
init_uvars();
|
|
|
|
}
|
2013-08-04 23:07:37 +00:00
|
|
|
|
|
|
|
explicit imp(std::shared_ptr<imp> const & parent):
|
|
|
|
m_num_children(0),
|
|
|
|
m_parent(parent) {
|
|
|
|
m_parent->inc_children();
|
|
|
|
}
|
|
|
|
|
|
|
|
~imp() {
|
|
|
|
if (m_parent)
|
|
|
|
m_parent->dec_children();
|
2013-08-05 03:52:14 +00:00
|
|
|
std::for_each(m_objects.begin(), m_objects.end(), [](object * obj) { delete obj; });
|
2013-08-04 23:07:37 +00:00
|
|
|
}
|
2013-07-29 05:34:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
environment::environment():
|
2013-08-04 23:07:37 +00:00
|
|
|
m_imp(new imp()) {
|
|
|
|
}
|
|
|
|
|
|
|
|
environment::environment(imp * new_ptr):
|
|
|
|
m_imp(new_ptr) {
|
|
|
|
}
|
|
|
|
|
|
|
|
environment::environment(std::shared_ptr<imp> const & ptr):
|
|
|
|
m_imp(ptr) {
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
environment::~environment() {
|
|
|
|
}
|
|
|
|
|
|
|
|
level environment::define_uvar(name const & n, level const & l) {
|
|
|
|
return m_imp->define_uvar(n, l);
|
|
|
|
}
|
|
|
|
|
2013-07-30 02:05:43 +00:00
|
|
|
bool environment::is_ge(level const & l1, level const & l2) const {
|
|
|
|
return m_imp->is_ge(l1, l2);
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void environment::display_uvars(std::ostream & out) const {
|
|
|
|
m_imp->display_uvars(out);
|
|
|
|
}
|
2013-08-04 23:07:37 +00:00
|
|
|
|
|
|
|
environment environment::mk_child() const {
|
|
|
|
return environment(new imp(m_imp));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool environment::has_children() const {
|
|
|
|
return m_imp->has_children();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool environment::has_parent() const {
|
|
|
|
return m_imp->has_parent();
|
|
|
|
}
|
|
|
|
|
|
|
|
environment environment::parent() const {
|
|
|
|
lean_assert(has_parent());
|
|
|
|
return environment(m_imp->m_parent);
|
|
|
|
}
|
|
|
|
|
2013-08-05 00:47:54 +00:00
|
|
|
level environment::get_uvar(name const & n) const {
|
|
|
|
return m_imp->get_uvar(n);
|
|
|
|
}
|
|
|
|
|
2013-08-05 03:52:14 +00:00
|
|
|
void environment::add_definition(name const & n, expr const & t, expr const & v, bool opaque) {
|
|
|
|
m_imp->check_no_children();
|
|
|
|
m_imp->check_name(n);
|
|
|
|
infer_universe(t, *this);
|
|
|
|
expr v_t = infer_type(v, *this);
|
|
|
|
if (!is_convertible(t, v_t, *this)) {
|
|
|
|
std::ostringstream buffer;
|
|
|
|
buffer << "type mismatch when defining '" << n << "'\n"
|
|
|
|
<< "expected type:\n" << t << "\n"
|
|
|
|
<< "given type:\n" << v_t;
|
|
|
|
throw exception(buffer.str());
|
|
|
|
}
|
|
|
|
m_imp->add_definition(n, t, v, opaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
void environment::add_definition(name const & n, expr const & v, bool opaque) {
|
|
|
|
m_imp->check_no_children();
|
|
|
|
m_imp->check_name(n);
|
|
|
|
expr v_t = infer_type(v, *this);
|
|
|
|
m_imp->add_definition(n, v_t, v, opaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
void environment::add_fact(name const & n, expr const & t) {
|
|
|
|
m_imp->check_no_children();
|
|
|
|
m_imp->check_name(n);
|
|
|
|
infer_universe(t, *this);
|
|
|
|
m_imp->add_fact(n, t);
|
|
|
|
}
|
|
|
|
|
|
|
|
environment::object const & environment::get_object(name const & n) const {
|
|
|
|
return m_imp->get_object(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
environment::object const * environment::get_object_ptr(name const & n) const {
|
|
|
|
return m_imp->get_object_ptr(n);
|
|
|
|
}
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|