feat(*): add [quasireducible] attribute

This commit is contained in:
Leonardo de Moura 2015-03-04 22:12:49 -08:00
parent 53df3d86ee
commit abd238aef0
17 changed files with 203 additions and 135 deletions

View file

@ -118,7 +118,7 @@
(,(rx symbol-start "_" symbol-end) . 'font-lock-preprocessor-face)
;; modifiers
(,(rx (or "\[persistent\]" "\[notation\]" "\[visible\]" "\[instance\]" "\[class\]" "\[parsing-only\]"
"\[coercion\]" "\[reducible\]" "\[irreducible\]" "\[semireducible\]" "\[wf\]"
"\[coercion\]" "\[reducible\]" "\[irreducible\]" "\[semireducible\]" "\[quasireducible\]" "\[wf\]"
"\[whnf\]" "\[multiple-instances\]"
"\[decls\]" "\[declarations\]" "\[all-transparent\]" "\[coercions\]" "\[classes\]"
"\[notations\]" "\[abbreviations\]" "\[begin-end-hints\]" "\[tactic-hints\]" "\[reduce-hints\]"))

View file

@ -168,7 +168,7 @@ constraint mk_calc_proof_cnstr(environment const & env, options const & opts,
unifier_config new_cfg(cfg);
new_cfg.m_discard = false;
new_cfg.m_conservative = conservative;
new_cfg.m_kind = conservative ? unifier_kind::Conservative : unifier_kind::Liberal;
unify_result_seq seq = unify(env, cs_buffer.size(), cs_buffer.data(), ngen, substitution(), new_cfg);
auto p = seq.pull();
lean_assert(p);

View file

@ -291,6 +291,7 @@ struct decl_attributes {
bool m_is_reducible;
bool m_is_irreducible;
bool m_is_semireducible;
bool m_is_quasireducible;
bool m_is_class;
bool m_is_parsing_only;
bool m_has_multiple_instances;
@ -306,6 +307,7 @@ struct decl_attributes {
m_is_reducible = is_abbrev;
m_is_irreducible = false;
m_is_semireducible = false;
m_is_quasireducible = false;
m_is_class = false;
m_is_parsing_only = false;
m_has_multiple_instances = false;
@ -364,20 +366,25 @@ struct decl_attributes {
throw parser_error("invalid '[coercion]' attribute, coercions cannot be defined in contexts", pos);
m_is_coercion = true;
} else if (p.curr_is_token(get_reducible_tk())) {
if (m_is_irreducible || m_is_semireducible)
throw parser_error("invalid '[reducible]' attribute, '[irreducible]' or '[semireducible]' was already provided", pos);
if (m_is_irreducible || m_is_semireducible || m_is_quasireducible)
throw parser_error("invalid '[reducible]' attribute, '[irreducible]', '[quasireducible]' or '[semireducible]' was already provided", pos);
m_is_reducible = true;
p.next();
} else if (p.curr_is_token(get_irreducible_tk())) {
if (m_is_reducible || m_is_semireducible)
throw parser_error("invalid '[irreducible]' attribute, '[reducible]' or '[semireducible]' was already provided", pos);
if (m_is_reducible || m_is_semireducible || m_is_quasireducible)
throw parser_error("invalid '[irreducible]' attribute, '[reducible]', '[quasireducible]' or '[semireducible]' was already provided", pos);
m_is_irreducible = true;
p.next();
} else if (p.curr_is_token(get_semireducible_tk())) {
if (m_is_reducible || m_is_irreducible)
throw parser_error("invalid '[irreducible]' attribute, '[reducible]' or '[irreducible]' was already provided", pos);
if (m_is_reducible || m_is_irreducible || m_is_quasireducible)
throw parser_error("invalid '[irreducible]' attribute, '[reducible]', '[quasireducible]' or '[irreducible]' was already provided", pos);
m_is_semireducible = true;
p.next();
} else if (p.curr_is_token(get_quasireducible_tk())) {
if (m_is_reducible || m_is_irreducible || m_is_semireducible)
throw parser_error("invalid '[quasireducible]' attribute, '[reducible]', '[semireducible]' or '[irreducible]' was already provided", pos);
m_is_quasireducible = true;
p.next();
} else if (p.curr_is_token(get_class_tk())) {
if (m_def_only)
throw parser_error("invalid '[class]' attribute, definitions cannot be marked as classes", pos);
@ -451,6 +458,8 @@ struct decl_attributes {
env = set_reducible(env, d, reducible_status::Irreducible, persistent);
if (m_is_semireducible)
env = set_reducible(env, d, reducible_status::Semireducible, persistent);
if (m_is_quasireducible)
env = set_reducible(env, d, reducible_status::Quasireducible, persistent);
if (m_is_class)
env = add_class(env, d, persistent);
if (m_has_multiple_instances)

View file

@ -71,7 +71,7 @@ bool match_pattern(type_checker & tc, expr const & pattern, declaration const &
try {
unifier_config cfg;
cfg.m_max_steps = max_steps;
cfg.m_cheap = cheap;
cfg.m_kind = cheap ? unifier_kind::Cheap : unifier_kind::Liberal;
cfg.m_ignore_context_check = true;
auto r = unify(tc.env(), pattern, dt, tc.mk_ngen(), true, substitution(), cfg);
return static_cast<bool>(r.pull());

View file

@ -766,7 +766,7 @@ bool match_type(type_checker & tc, expr const & meta, expr const & expected_type
try {
unifier_config cfg;
cfg.m_max_steps = LEAN_FINDG_MAX_STEPS;
cfg.m_cheap = true;
cfg.m_kind = unifier_kind::Cheap;
auto r = unify(tc.env(), dt, expected_type, tc.mk_ngen(), true, substitution(), cfg);
return static_cast<bool>(r.pull());
} catch (exception&) {

View file

@ -90,7 +90,7 @@ void init_token_table(token_table & t) {
{"theorem", "axiom", "variable", "protected", "private", "opaque", "definition", "example", "coercion",
"abbreviation",
"variables", "parameter", "parameters", "constant", "constants", "[persistent]", "[visible]", "[instance]",
"[off]", "[on]", "[none]", "[class]", "[coercion]", "[reducible]", "[irreducible]", "[semireducible]",
"[off]", "[on]", "[none]", "[class]", "[coercion]", "[reducible]", "[irreducible]", "[semireducible]", "[quasireducible]",
"[parsing-only]", "[multiple-instances]",
"evaluate", "check", "eval", "[wf]", "[whnf]", "[all-transparent]", "[priority", "[unfold-c", "print",
"end", "namespace", "section", "prelude",

View file

@ -100,6 +100,7 @@ static name * g_priority = nullptr;
static name * g_unfold_c = nullptr;
static name * g_coercion = nullptr;
static name * g_reducible = nullptr;
static name * g_quasireducible = nullptr;
static name * g_semireducible = nullptr;
static name * g_irreducible = nullptr;
static name * g_parsing_only = nullptr;
@ -220,6 +221,7 @@ void initialize_tokens() {
g_unfold_c = new name("[unfold-c");
g_coercion = new name("[coercion]");
g_reducible = new name("[reducible]");
g_quasireducible = new name("[quasireducible]");
g_semireducible = new name("[semireducible]");
g_irreducible = new name("[irreducible]");
g_parsing_only = new name("[parsing-only]");
@ -282,6 +284,7 @@ void finalize_tokens() {
delete g_unfold_c;
delete g_coercion;
delete g_reducible;
delete g_quasireducible;
delete g_semireducible;
delete g_irreducible;
delete g_multiple_instances;
@ -462,6 +465,7 @@ name const & get_priority_tk() { return *g_priority; }
name const & get_unfold_c_tk() { return *g_unfold_c; }
name const & get_coercion_tk() { return *g_coercion; }
name const & get_reducible_tk() { return *g_reducible; }
name const & get_quasireducible_tk() { return *g_quasireducible; }
name const & get_semireducible_tk() { return *g_semireducible; }
name const & get_irreducible_tk() { return *g_irreducible; }
name const & get_multiple_instances_tk() { return *g_multiple_instances; }

View file

@ -103,6 +103,7 @@ name const & get_unfold_c_tk();
name const & get_coercion_tk();
name const & get_reducible_tk();
name const & get_semireducible_tk();
name const & get_quasireducible_tk();
name const & get_irreducible_tk();
name const & get_multiple_instances_tk();
name const & get_attribute_tk();

View file

@ -21,27 +21,16 @@ struct reducible_entry {
reducible_entry(reducible_status s, name const & n):m_status(s), m_name(n) {}
};
struct reducible_state {
name_set m_reducible_on;
name_set m_reducible_off;
void reducible_state::add(reducible_entry const & e) {
m_status.insert(e.m_name, e.m_status);
}
void add(reducible_entry const & e) {
switch (e.m_status) {
case reducible_status::Reducible:
m_reducible_on.insert(e.m_name);
m_reducible_off.erase(e.m_name);
break;
case reducible_status::Irreducible:
m_reducible_on.erase(e.m_name);
m_reducible_off.insert(e.m_name);
break;
case reducible_status::Semireducible:
m_reducible_on.erase(e.m_name);
m_reducible_off.erase(e.m_name);
break;
}
}
};
reducible_status reducible_state::get_status(name const & n) const {
if (auto it = m_status.find(n))
return *it;
else
return reducible_status::Semireducible;
}
static name * g_class_name = nullptr;
static std::string * g_key = nullptr;
@ -106,46 +95,64 @@ environment set_reducible(environment const & env, name const & n, reducible_sta
return reducible_ext::add_entry(env, get_dummy_ios(), reducible_entry(s, n), persistent);
}
bool is_reducible_on(environment const & env, name const & n) {
reducible_status get_reducible_status(environment const & env, name const & n) {
reducible_state const & s = reducible_ext::get_state(env);
return s.m_reducible_on.contains(n);
return s.get_status(n);
}
bool is_reducible_off(environment const & env, name const & n) {
reducible_state const & s = reducible_ext::get_state(env);
return s.m_reducible_off.contains(n);
bool is_at_least_quasireducible(environment const & env, name const & n) {
reducible_status r = get_reducible_status(env, n);
return r == reducible_status::Reducible || r == reducible_status::Quasireducible;
}
reducible_on_converter::reducible_on_converter(environment const & env, bool relax_main_opaque, bool memoize):
unfold_reducible_converter::unfold_reducible_converter(environment const & env, bool relax_main_opaque, bool memoize):
default_converter(env, relax_main_opaque, memoize) {
m_reducible_on = reducible_ext::get_state(env).m_reducible_on;
m_state = reducible_ext::get_state(env);
}
bool reducible_on_converter::is_opaque(declaration const & d) const {
if (!m_reducible_on.contains(d.get_name())) return true;
bool unfold_reducible_converter::is_opaque(declaration const & d) const {
auto r = m_state.get_status(d.get_name());
if (r != reducible_status::Reducible) return true;
return default_converter::is_opaque(d);
}
reducible_off_converter::reducible_off_converter(environment const & env, bool relax_main_opaque, bool memoize):
unfold_quasireducible_converter::unfold_quasireducible_converter(environment const & env, bool relax_main_opaque, bool memoize):
default_converter(env, relax_main_opaque, memoize) {
m_reducible_off = reducible_ext::get_state(env).m_reducible_off;
m_state = reducible_ext::get_state(env);
}
bool reducible_off_converter::is_opaque(declaration const & d) const {
if (m_reducible_off.contains(d.get_name())) return true;
bool unfold_quasireducible_converter::is_opaque(declaration const & d) const {
auto r = m_state.get_status(d.get_name());
if (r != reducible_status::Reducible && r != reducible_status::Quasireducible) return true;
return default_converter::is_opaque(d);
}
unfold_semireducible_converter::unfold_semireducible_converter(environment const & env, bool relax_main_opaque, bool memoize):
default_converter(env, relax_main_opaque, memoize) {
m_state = reducible_ext::get_state(env);
}
bool unfold_semireducible_converter::is_opaque(declaration const & d) const {
auto r = m_state.get_status(d.get_name());
if (r == reducible_status::Irreducible) return true;
return default_converter::is_opaque(d);
}
std::unique_ptr<type_checker> mk_type_checker(environment const & env, name_generator const & ngen,
bool relax_main_opaque, reducible_behavior rb,
bool memoize) {
if (rb == OpaqueIfNotReducibleOn) {
switch (rb) {
case UnfoldReducible:
return std::unique_ptr<type_checker>(new type_checker(env, ngen,
std::unique_ptr<converter>(new reducible_on_converter(env, relax_main_opaque, memoize))));
} else {
std::unique_ptr<converter>(new unfold_reducible_converter(env, relax_main_opaque, memoize))));
case UnfoldQuasireducible:
return std::unique_ptr<type_checker>(new type_checker(env, ngen,
std::unique_ptr<converter>(new reducible_off_converter(env, relax_main_opaque, memoize))));
std::unique_ptr<converter>(new unfold_quasireducible_converter(env, relax_main_opaque, memoize))));
case UnfoldSemireducible:
return std::unique_ptr<type_checker>(new type_checker(env, ngen,
std::unique_ptr<converter>(new unfold_semireducible_converter(env, relax_main_opaque, memoize))));
}
lean_unreachable();
}
std::unique_ptr<type_checker> mk_type_checker(environment const & env, bool relax_main_opaque, reducible_behavior rb) {
@ -192,19 +199,11 @@ static int mk_reducible_checker_core(lua_State * L, reducible_behavior rb) {
}
static int mk_reducible_type_checker(lua_State * L) {
return mk_reducible_checker_core(L, OpaqueIfNotReducibleOn);
return mk_reducible_checker_core(L, UnfoldReducible);
}
static int mk_non_irreducible_type_checker(lua_State * L) {
return mk_reducible_checker_core(L, OpaqueIfReducibleOff);
}
static int is_reducible_on(lua_State * L) {
return push_boolean(L, is_reducible_on(to_environment(L, 1), to_name_ext(L, 2)));
}
static int is_reducible_off(lua_State * L) {
return push_boolean(L, is_reducible_off(to_environment(L, 1), to_name_ext(L, 2)));
return mk_reducible_checker_core(L, UnfoldSemireducible);
}
static int set_reducible(lua_State * L) {
@ -221,12 +220,11 @@ static int set_reducible(lua_State * L) {
void open_reducible(lua_State * L) {
lua_newtable(L);
SET_ENUM("On", reducible_status::Reducible);
SET_ENUM("Off", reducible_status::Irreducible);
SET_ENUM("None", reducible_status::Semireducible);
SET_ENUM("Reducible", reducible_status::Reducible);
SET_ENUM("QuasiReducible", reducible_status::Quasireducible);
SET_ENUM("SemiReducible", reducible_status::Semireducible);
SET_ENUM("Irreducible", reducible_status::Irreducible);
lua_setglobal(L, "reducible_status");
SET_GLOBAL_FUN(is_reducible_on, "is_reducible_on");
SET_GLOBAL_FUN(is_reducible_off, "is_reducible_off");
SET_GLOBAL_FUN(set_reducible, "set_reducible");
SET_GLOBAL_FUN(mk_opaque_type_checker, "opaque_type_checker");
SET_GLOBAL_FUN(mk_non_irreducible_type_checker, "non_irreducible_type_checker");

View file

@ -10,7 +10,7 @@ Author: Leonardo de Moura
#include "kernel/default_converter.h"
namespace lean {
enum class reducible_status { Reducible, Irreducible, Semireducible };
enum class reducible_status { Reducible, Quasireducible, Semireducible, Irreducible };
/** \brief Mark the definition named \c n as reducible or not.
The method throws an exception if \c n is
@ -22,40 +22,52 @@ enum class reducible_status { Reducible, Irreducible, Semireducible };
We should view it as a hint to automation.
*/
environment set_reducible(environment const & env, name const & n, reducible_status s, bool persistent = true);
/** \brief Return true iff \c n was explicitly marked as reducible in the given environment.
\remark is_reducible_on(env, n) and is_reducible_off(env, n) cannot be simultaneously true,
but both can be false (when set_reducible(env, n, None))
*/
bool is_reducible_on(environment const & env, name const & n);
/** \brief Return true iff \c n was explicitly marked as not reducible in the given environment.
reducible_status get_reducible_status(environment const & env, name const & n);
\see is_reducible_on
*/
bool is_reducible_off(environment const & env, name const & n);
bool is_at_least_quasireducible(environment const & env, name const & n);
class reducible_on_converter : public default_converter {
name_set m_reducible_on;
struct reducible_entry;
class reducible_state {
name_map<reducible_status> m_status;
public:
reducible_on_converter(environment const & env, bool relax_main_opaque, bool memoize);
void add(reducible_entry const & e);
reducible_status get_status(name const & n) const;
};
/** \brief Unfold only constants marked as reducible */
class unfold_reducible_converter : public default_converter {
reducible_state m_state;
public:
unfold_reducible_converter(environment const & env, bool relax_main_opaque, bool memoize);
virtual bool is_opaque(declaration const & d) const;
};
class reducible_off_converter : public default_converter {
name_set m_reducible_off;
/** \brief Unfold only constants marked as reducible or quasireducible */
class unfold_quasireducible_converter : public default_converter {
reducible_state m_state;
public:
reducible_off_converter(environment const & env, bool relax_main_opaque, bool memoize);
unfold_quasireducible_converter(environment const & env, bool relax_main_opaque, bool memoize);
virtual bool is_opaque(declaration const & d) const;
};
enum reducible_behavior { OpaqueIfNotReducibleOn, OpaqueIfReducibleOff };
/** \brief Unfold only constants marked as reducible, quasireducible, or semireducible */
class unfold_semireducible_converter : public default_converter {
reducible_state m_state;
public:
unfold_semireducible_converter(environment const & env, bool relax_main_opaque, bool memoize);
virtual bool is_opaque(declaration const & d) const;
};
enum reducible_behavior { UnfoldReducible, UnfoldQuasireducible, UnfoldSemireducible };
/** \brief Create a type checker that takes the "reducibility" hints into account. */
std::unique_ptr<type_checker> mk_type_checker(environment const & env, name_generator const & ngen,
bool relax_main_opaque = true, reducible_behavior r = OpaqueIfReducibleOff,
bool relax_main_opaque = true, reducible_behavior r = UnfoldSemireducible,
bool memoize = true);
std::unique_ptr<type_checker> mk_type_checker(environment const & env, bool relax_main_opaque,
reducible_behavior r = OpaqueIfReducibleOff);
reducible_behavior r = UnfoldSemireducible);
/** \brief Create a type checker that treats all definitions as opaque. */
std::unique_ptr<type_checker> mk_opaque_type_checker(environment const & env, name_generator const & ngen);

View file

@ -117,7 +117,7 @@ struct class_instance_context {
m_max_depth = get_class_instance_max_depth(ios.get_options());
m_conservative = get_class_conservative(ios.get_options());
if (m_conservative)
m_tc = mk_type_checker(env, m_ngen.mk_child(), false, OpaqueIfNotReducibleOn);
m_tc = mk_type_checker(env, m_ngen.mk_child(), false, UnfoldReducible);
else
m_tc = mk_type_checker(env, m_ngen.mk_child(), m_relax);
options opts = m_ios.get_options();
@ -350,7 +350,7 @@ constraint mk_class_instance_root_cnstr(std::shared_ptr<class_instance_context>
new_cfg.m_discard = false;
new_cfg.m_use_exceptions = false;
new_cfg.m_pattern = true;
new_cfg.m_conservative = C->m_conservative;
new_cfg.m_kind = C->m_conservative ? unifier_kind::VeryConservative : unifier_kind::Liberal;
auto to_cnstrs_fn = [=](substitution const & subst, constraints const & cnstrs) -> constraints {
substitution new_s = subst;
@ -463,7 +463,7 @@ optional<expr> mk_class_instance(environment const & env, io_state const & ios,
new_cfg.m_discard = true;
new_cfg.m_use_exceptions = true;
new_cfg.m_pattern = true;
new_cfg.m_conservative = C->m_conservative;
new_cfg.m_kind = C->m_conservative ? unifier_kind::VeryConservative : unifier_kind::Liberal;
try {
auto p = unify(env, 1, &c, C->m_ngen.mk_child(), substitution(), new_cfg).pull();
lean_assert(p);

View file

@ -815,7 +815,7 @@ class rewrite_fn {
pair<expr, constraint> mk_class_instance_elaborator(expr const & type) {
unifier_config cfg;
cfg.m_conservative = true;
cfg.m_kind = unifier_kind::VeryConservative;
bool use_local_instances = true;
bool is_strict = false;
return ::lean::mk_class_instance_elaborator(m_env, m_ios, m_ctx, m_ngen.next(), optional<name>(),
@ -861,7 +861,7 @@ class rewrite_fn {
return unify_result();
cs_seq.linearize(cs);
unifier_config cfg;
cfg.m_conservative = false;
cfg.m_kind = unifier_kind::Liberal;
cfg.m_discard = true;
unify_result_seq rseq = unify(m_env, cs.size(), cs.data(), m_ngen.mk_child(), m_subst, cfg);
if (auto p = rseq.pull()) {
@ -1128,14 +1128,14 @@ class rewrite_fn {
}
}
class match_converter : public reducible_on_converter {
class match_converter : public unfold_reducible_converter {
public:
match_converter(environment const & env, bool relax_main_opaque):
reducible_on_converter(env, relax_main_opaque, true) {}
unfold_reducible_converter(env, relax_main_opaque, true) {}
virtual bool is_opaque(declaration const & d) const {
if (is_projection(m_env, d.get_name()))
return true;
return reducible_on_converter::is_opaque(d);
return unfold_reducible_converter::is_opaque(d);
}
};
@ -1152,7 +1152,7 @@ class rewrite_fn {
public:
rewrite_fn(environment const & env, io_state const & ios, elaborate_fn const & elab, proof_state const & ps):
m_env(env), m_ios(ios), m_elab(elab), m_ps(ps), m_ngen(ps.get_ngen()),
m_tc(mk_type_checker(m_env, m_ngen.mk_child(), ps.relax_main_opaque(), OpaqueIfNotReducibleOn)),
m_tc(mk_type_checker(m_env, m_ngen.mk_child(), ps.relax_main_opaque(), UnfoldQuasireducible)),
m_matcher_tc(mk_matcher_tc()),
m_unifier_tc(mk_type_checker(m_env, m_ngen.mk_child(), ps.relax_main_opaque())),
m_mplugin(m_ios, *m_matcher_tc) {

View file

@ -76,10 +76,9 @@ unifier_config::unifier_config(bool use_exceptions, bool discard):
m_max_steps(LEAN_DEFAULT_UNIFIER_MAX_STEPS),
m_computation(LEAN_DEFAULT_UNIFIER_COMPUTATION),
m_expensive_classes(LEAN_DEFAULT_UNIFIER_EXPENSIVE_CLASSES),
m_discard(discard),
m_conservative(LEAN_DEFAULT_UNIFIER_CONSERVATIVE) {
m_discard(discard) {
m_kind = unifier_kind::Liberal;
m_pattern = false;
m_cheap = false;
m_ignore_context_check = false;
}
@ -88,9 +87,11 @@ unifier_config::unifier_config(options const & o, bool use_exceptions, bool disc
m_max_steps(get_unifier_max_steps(o)),
m_computation(get_unifier_computation(o)),
m_expensive_classes(get_unifier_expensive_classes(o)),
m_discard(discard),
m_conservative(get_unifier_conservative(o)) {
m_cheap = false;
m_discard(discard) {
if (get_unifier_conservative(o))
m_kind = unifier_kind::Conservative;
else
m_kind = unifier_kind::Liberal;
m_pattern = false;
m_ignore_context_check = false;
}
@ -425,21 +426,33 @@ struct unifier_fn {
unifier_config const & cfg):
m_env(env), m_ngen(ngen), m_subst(s), m_plugin(get_unifier_plugin(env)),
m_config(cfg), m_num_steps(0) {
if (m_config.m_cheap) {
switch (m_config.m_kind) {
case unifier_kind::Cheap:
m_tc[0] = mk_opaque_type_checker(env, m_ngen.mk_child());
m_tc[1] = m_tc[0];
m_flex_rigid_tc = m_tc[0];
m_config.m_computation = false;
} else if (m_config.m_conservative) {
m_tc[0] = mk_type_checker(env, m_ngen.mk_child(), false, OpaqueIfNotReducibleOn);
break;
case unifier_kind::VeryConservative:
m_tc[0] = mk_type_checker(env, m_ngen.mk_child(), false, UnfoldReducible);
m_tc[1] = m_tc[0];
m_flex_rigid_tc = m_tc[0];
m_config.m_computation = false;
} else {
break;
case unifier_kind::Conservative:
m_tc[0] = mk_type_checker(env, m_ngen.mk_child(), false, UnfoldQuasireducible);
m_tc[1] = m_tc[0];
m_flex_rigid_tc = m_tc[0];
m_config.m_computation = false;
break;
case unifier_kind::Liberal:
m_tc[0] = mk_type_checker(env, m_ngen.mk_child(), false);
m_tc[1] = mk_type_checker(env, m_ngen.mk_child(), true);
if (!cfg.m_computation)
m_flex_rigid_tc = mk_type_checker(env, m_ngen.mk_child(), false, OpaqueIfNotReducibleOn);
m_flex_rigid_tc = mk_type_checker(env, m_ngen.mk_child(), false, UnfoldQuasireducible);
break;
default:
lean_unreachable();
}
m_next_assumption_idx = 0;
m_next_cidx = 0;
@ -1346,8 +1359,8 @@ struct unifier_fn {
justification a;
bool relax = relax_main_opaque(c);
if (!m_config.m_cheap && !m_config.m_conservative &&
(m_config.m_computation || module::is_definition(m_env, d.get_name()) || is_reducible_on(m_env, d.get_name()))) {
if (m_config.m_kind == unifier_kind::Liberal &&
(m_config.m_computation || module::is_definition(m_env, d.get_name()) || is_at_least_quasireducible(m_env, d.get_name()))) {
// add case_split for t =?= s
a = mk_assumption_justification(m_next_assumption_idx);
add_case_split(std::unique_ptr<case_split>(new delta_unfold_case_split(*this, j, c)));
@ -1409,7 +1422,7 @@ struct unifier_fn {
optional<bool> _has_meta_args;
bool cheap() const { return u.m_config.m_cheap; }
bool cheap() const { return u.m_config.m_kind == unifier_kind::Cheap; }
bool pattern() const { return u.m_config.m_pattern; }
type_checker & tc() {
@ -1898,7 +1911,7 @@ struct unifier_fn {
*/
bool use_flex_rigid_whnf_split(expr const & lhs, expr const & rhs) {
lean_assert(is_meta(lhs));
if (m_config.m_cheap || m_config.m_conservative)
if (m_config.m_kind != unifier_kind::Liberal)
return false;
if (m_config.m_computation)
return true; // if unifier.computation is true, we always consider the additional whnf split

View file

@ -32,6 +32,8 @@ unify_status unify_simple(substitution & s, expr const & lhs, expr const & rhs,
unify_status unify_simple(substitution & s, level const & lhs, level const & rhs, justification const & j);
unify_status unify_simple(substitution & s, constraint const & c);
enum class unifier_kind { Cheap, VeryConservative, Conservative, Liberal };
struct unifier_config {
bool m_use_exceptions;
unsigned m_max_steps;
@ -40,20 +42,25 @@ struct unifier_config {
// If m_discard is true, then constraints that cannot be solved are discarded (or incomplete methods are used)
// If m_discard is false, unify returns the set of constraints that could not be handled.
bool m_discard;
// If m_conservative is true, then the following restrictions are imposed:
// - All constants that are not marked as reducible as treated as
// If m_mind == Conservative, then the following restrictions are imposed:
// - All constants that are not at least marked as Quasireducible as treated as
// opaque.
// - Disables case-split on delta-delta constraints.
// - Disables reduction case-split on flex-rigid constraints.
// Default is m_conservative == false
bool m_conservative;
//
// If m_kind == VeryConservative, then
// - More restrictive than Conservative,
// - All constants that are not at least marked as Reducible as treated as
// opaque.
//
// If m_kind == Cheap is true, then expensive case-analysis is not performed (e.g., delta).
// It is more restrictive than VeryConservative
//
// Default is Liberal
unifier_kind m_kind;
// If m_pattern is true, then we restrict the number of cases splits on
// flex-rigid constraints that are *not* in the higher-order pattern case.
bool m_pattern;
// If m_cheap is true, then expensive case-analysis is not performed (e.g., delta).
// It is more restrictive than m_conservative
// Default is m_cheap == false
bool m_cheap;
// If m_ignore_context_check == true, then occurs-check is skipped.
// Default is m_ignore_context_check == false
bool m_ignore_context_check;

View file

@ -0,0 +1,21 @@
constant g : nat → nat
definition f [reducible] := g
example (a : nat) (H : f a = a) : g a = a :=
by rewrite H
attribute f [quasireducible]
example (a : nat) (H : f a = a) : g a = a :=
by rewrite H -- error
attribute f [semireducible]
example (a : nat) (H : f a = a) : g a = a :=
by rewrite H -- error
attribute f [reducible]
example (a : nat) (H : f a = a) : g a = a :=
by rewrite H -- error

View file

@ -0,0 +1,24 @@
quasireducible.lean:11:11: error:invalid 'rewrite' tactic, rewrite step failed
proof state:
a : nat,
H : f a = a
⊢ g a = a
quasireducible.lean:11:3: error: don't know how to synthesize placeholder
a : nat,
H : f a = a
⊢ g a = a
quasireducible.lean:11:3: error: failed to add declaration '14.1' to environment, value has metavariables
λ (a : nat) (H : f a = a),
?M_1
quasireducible.lean:16:11: error:invalid 'rewrite' tactic, rewrite step failed
proof state:
a : nat,
H : f a = a
⊢ g a = a
quasireducible.lean:16:3: error: don't know how to synthesize placeholder
a : nat,
H : f a = a
⊢ g a = a
quasireducible.lean:16:3: error: failed to add declaration '14.2' to environment, value has metavariables
λ (a : nat) (H : f a = a),
?M_1

View file

@ -40,25 +40,4 @@ opaque definition w := 40
assert(tc:whnf(w) == w)
*)
(*
local env = get_env()
assert(is_reducible_on(env, "x"))
assert(not is_reducible_on(env, "y"))
assert(not is_reducible_on(env, "z"))
assert(not is_reducible_off(env, "x"))
assert(not is_reducible_off(env, "y"))
assert(is_reducible_off(env, "z"))
env = set_reducible(env, "x", reducible_status.Off)
assert(not is_reducible_on(env, "x"))
assert(is_reducible_off(env, "x"))
env = set_reducible(env, "x", reducible_status.None)
assert(not is_reducible_on(env, "x"))
assert(not is_reducible_off(env, "x"))
env = set_reducible(env, "x", reducible_status.On)
assert(is_reducible_on(env, "x"))
assert(not is_reducible_off(env, "x"))
env = set_reducible(env, "x", reducible_status.Off)
set_env(env)
*)
eval [whnf] x