fix(library/definitional/equations): add more equation validation to avoid obscure error message
This commit is contained in:
parent
2e4a2451e6
commit
a3bc1b0cd5
3 changed files with 23 additions and 5 deletions
|
@ -315,6 +315,7 @@ class equation_compiler_fn {
|
||||||
[[ noreturn ]] static void throw_error(char const * msg, expr const & src) { throw_generic_exception(msg, src); }
|
[[ noreturn ]] static void throw_error(char const * msg, expr const & src) { throw_generic_exception(msg, src); }
|
||||||
[[ noreturn ]] static void throw_error(expr const & src, pp_fn const & fn) { throw_generic_exception(src, fn); }
|
[[ noreturn ]] static void throw_error(expr const & src, pp_fn const & fn) { throw_generic_exception(src, fn); }
|
||||||
[[ noreturn ]] void throw_error(sstream const & ss) const { throw_generic_exception(ss, m_meta); }
|
[[ noreturn ]] void throw_error(sstream const & ss) const { throw_generic_exception(ss, m_meta); }
|
||||||
|
[[ noreturn ]] void throw_error(expr const & src, sstream const & ss) const { throw_generic_exception(ss, src); }
|
||||||
|
|
||||||
void check_limitations(expr const & eqns) const {
|
void check_limitations(expr const & eqns) const {
|
||||||
if (is_wf_equations(eqns) && equations_num_fns(eqns) != 1)
|
if (is_wf_equations(eqns) && equations_num_fns(eqns) != 1)
|
||||||
|
@ -478,19 +479,27 @@ class equation_compiler_fn {
|
||||||
validate_exception(expr const & e):m_expr(e) {}
|
validate_exception(expr const & e):m_expr(e) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void check_in_local_ctx(expr const & e, buffer<expr> const & local_ctx) {
|
||||||
|
if (!contains_local(e, local_ctx))
|
||||||
|
throw_error(e, sstream() << "invalid recursive equation, variable '" << e
|
||||||
|
<< "' has the same name of a variable in an outer-scope (solution: rename this variable)");
|
||||||
|
}
|
||||||
|
|
||||||
// Validate/normalize the given pattern.
|
// Validate/normalize the given pattern.
|
||||||
// It stores in reachable_vars any variable that does not occur
|
// It stores in reachable_vars any variable that does not occur
|
||||||
// in inaccessible terms.
|
// in inaccessible terms.
|
||||||
expr validate_pattern(expr pat, name_set & reachable_vars) {
|
expr validate_pattern(expr pat, buffer<expr> const & local_ctx, name_set & reachable_vars) {
|
||||||
if (is_inaccessible(pat))
|
if (is_inaccessible(pat))
|
||||||
return pat;
|
return pat;
|
||||||
if (is_local(pat)) {
|
if (is_local(pat)) {
|
||||||
reachable_vars.insert(mlocal_name(pat));
|
reachable_vars.insert(mlocal_name(pat));
|
||||||
|
check_in_local_ctx(pat, local_ctx);
|
||||||
return pat;
|
return pat;
|
||||||
}
|
}
|
||||||
expr new_pat = whnf(pat);
|
expr new_pat = whnf(pat);
|
||||||
if (is_local(new_pat)) {
|
if (is_local(new_pat)) {
|
||||||
reachable_vars.insert(mlocal_name(new_pat));
|
reachable_vars.insert(mlocal_name(new_pat));
|
||||||
|
check_in_local_ctx(new_pat, local_ctx);
|
||||||
return new_pat;
|
return new_pat;
|
||||||
}
|
}
|
||||||
buffer<expr> pat_args;
|
buffer<expr> pat_args;
|
||||||
|
@ -498,7 +507,7 @@ class equation_compiler_fn {
|
||||||
if (auto in = is_constructor(fn)) {
|
if (auto in = is_constructor(fn)) {
|
||||||
unsigned num_params = *inductive::get_num_params(env(), *in);
|
unsigned num_params = *inductive::get_num_params(env(), *in);
|
||||||
for (unsigned i = num_params; i < pat_args.size(); i++)
|
for (unsigned i = num_params; i < pat_args.size(); i++)
|
||||||
pat_args[i] = validate_pattern(pat_args[i], reachable_vars);
|
pat_args[i] = validate_pattern(pat_args[i], local_ctx, reachable_vars);
|
||||||
return mk_app(fn, pat_args, pat.get_tag());
|
return mk_app(fn, pat_args, pat.get_tag());
|
||||||
} else {
|
} else {
|
||||||
throw validate_exception(pat);
|
throw validate_exception(pat);
|
||||||
|
@ -509,10 +518,10 @@ class equation_compiler_fn {
|
||||||
// The lhs is only used to report errors.
|
// The lhs is only used to report errors.
|
||||||
// It stores in reachable_vars any variable that does not occur
|
// It stores in reachable_vars any variable that does not occur
|
||||||
// in inaccessible terms.
|
// in inaccessible terms.
|
||||||
void validate_patterns(expr const & lhs, buffer<expr> & patterns, name_set & reachable_vars) {
|
void validate_patterns(expr const & lhs, buffer<expr> const & local_ctx, buffer<expr> & patterns, name_set & reachable_vars) {
|
||||||
for (expr & pat : patterns) {
|
for (expr & pat : patterns) {
|
||||||
try {
|
try {
|
||||||
pat = validate_pattern(pat, reachable_vars);
|
pat = validate_pattern(pat, local_ctx, reachable_vars);
|
||||||
} catch (validate_exception & ex) {
|
} catch (validate_exception & ex) {
|
||||||
expr problem_expr = ex.m_expr;
|
expr problem_expr = ex.m_expr;
|
||||||
throw_error(lhs, [=](formatter const & fmt) {
|
throw_error(lhs, [=](formatter const & fmt) {
|
||||||
|
@ -546,7 +555,7 @@ class equation_compiler_fn {
|
||||||
buffer<expr> patterns;
|
buffer<expr> patterns;
|
||||||
expr const & fn = get_app_args(lhs, patterns);
|
expr const & fn = get_app_args(lhs, patterns);
|
||||||
name_set reachable_vars;
|
name_set reachable_vars;
|
||||||
validate_patterns(lhs, patterns, reachable_vars);
|
validate_patterns(lhs, local_ctx, patterns, reachable_vars);
|
||||||
for (expr const & v : local_ctx) {
|
for (expr const & v : local_ctx) {
|
||||||
// every variable in the local_ctx must be "reachable".
|
// every variable in the local_ctx must be "reachable".
|
||||||
if (!reachable_vars.contains(mlocal_name(v))) {
|
if (!reachable_vars.contains(mlocal_name(v))) {
|
||||||
|
|
8
tests/lean/shadow.lean
Normal file
8
tests/lean/shadow.lean
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
open nat
|
||||||
|
|
||||||
|
variable a : nat
|
||||||
|
|
||||||
|
-- The variable 'a' in the following definition is not the variable 'a' above
|
||||||
|
definition tadd : nat → nat → nat,
|
||||||
|
tadd zero b := b,
|
||||||
|
tadd (succ a) b := succ (tadd a b)
|
1
tests/lean/shadow.lean.expected.out
Normal file
1
tests/lean/shadow.lean.expected.out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
shadow.lean:8:11: error: invalid recursive equation, variable 'a' has the same name of a variable in an outer-scope (solution: rename this variable)
|
Loading…
Reference in a new issue