lean4-htt/src/frontends/lean/equations_validator.cpp

142 lines
4.8 KiB
C++

/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include "library/replace_visitor.h"
#include "library/inverse.h"
#include "library/inductive_compiler/ginductive.h"
#include "library/equations_compiler/equations.h"
#include "library/equations_compiler/util.h"
#include "library/tactic/unfold_tactic.h"
#include "frontends/lean/elaborator.h"
namespace lean {
/* Recursive equation pattern validation.*/
class validate_equation_lhs_fn : public replace_visitor {
elaborator & m_elab;
expr m_ref;
type_context & ctx() { return m_elab.m_ctx; }
environment const & env() { return m_elab.env(); }
optional<expr> expand(expr const & e) {
/* We don't simply use whnf because we want to avoid exposing the internal implementation
of definitions compiled using the equation compiler */
{
/* Try without use delta reduction */
type_context::transparency_scope scope(ctx(), transparency_mode::None);
expr new_e = ctx().whnf(e);
if (new_e != e) return some_expr(new_e);
}
if (auto new_e = ctx().reduce_projection(e))
return new_e;
/* Try to unfold using refl equations */
if (auto new_e = dunfold(ctx(), e))
return new_e;
/* Last resort, whnf using current setting */
expr new_e = ctx().whnf(e);
if (new_e != e) return some_expr(new_e);
return none_expr();
}
[[ noreturn ]] void throw_invalid_pattern(char const * msg, expr const & e) {
throw elaborator_exception(m_ref, format(msg)
+ format(" (possible solution, mark term as inaccessible using '.( )')")
+ m_elab.pp_indent(e));
}
virtual expr visit_local(expr const & e) override {
return e;
}
virtual expr visit_lambda(expr const & e) override {
throw_invalid_pattern("invalid occurrence of lambda expression in pattern", e);
}
virtual expr visit_pi(expr const & e) override {
throw_invalid_pattern("invalid occurrence of pi/forall expression in pattern", e);
}
virtual expr visit_let(expr const & e) override {
return visit(instantiate(let_body(e), let_value(e)));
}
virtual expr visit_sort(expr const & e) override {
throw_invalid_pattern("invalid occurrence of sort in pattern", e);
}
virtual expr visit_meta(expr const & e) override {
throw_invalid_pattern("invalid occurrence of metavariable in pattern", e);
}
[[ noreturn ]] void throw_invalid_app(expr const & e) {
if (is_constant(e))
throw_invalid_pattern("invalid constant in pattern, it cannot be reduced to a constructor", e);
else
throw_invalid_pattern("invalid function application in pattern, it cannot be reduced to a constructor", e);
}
virtual expr visit_app(expr const & e) override {
expr it = e;
while (true) {
if (is_nat_int_char_string_name_value(ctx(), it))
return e;
if (!is_app(it) && !is_constant(it))
return visit(it);
buffer<expr> args;
expr const & fn = get_app_args(it, args);
if (!is_constant(fn))
throw_invalid_app(e);
if (optional<name> I = is_ginductive_intro_rule(env(), const_name(fn))) {
unsigned num_params = get_ginductive_num_params(env(), *I);
for (unsigned i = num_params; i < args.size(); i++) {
visit(args[i]);
}
return e;
} else if (optional<inverse_info> info = has_inverse(env(), const_name(fn))) {
visit(args.back());
return e;
} else {
if (auto r = expand(it)) {
it = *r;
} else {
throw_invalid_app(e);
}
}
}
}
virtual expr visit_constant(expr const & e) override {
return visit_app(e);
}
virtual expr visit_macro(expr const & e) override {
if (is_inaccessible(e)) {
return e;
} else if (auto r = ctx().expand_macro(e)) {
return visit(*r);
} else {
throw_invalid_pattern("invalid occurrence of macro expression in pattern", e);
}
}
public:
validate_equation_lhs_fn(elaborator & elab, expr const & ref):
m_elab(elab), m_ref(ref) {
}
void validate(expr const & lhs) {
buffer<expr> args;
get_app_args(lhs, args);
for (expr & arg : args)
visit(arg);
}
};
void validate_equation_lhs(elaborator & elab, expr const & lhs, expr const & ref) {
validate_equation_lhs_fn(elab, ref).validate(elab.instantiate_mvars(lhs));
}
}