ISLE: handle out-of-order extern converter decls. (#4079)

This fixes a bug where the ISLE compiler would refuse to accept
out-of-order declarations in the order of: (i) use of an implicit
conversion backed by an extern constructor; (ii) extern declaration
for that constructor.

The issue was one of phase separation: we were capturing and noting
"extern constructor" status on terms in the same pass in which we were
typechecking and resolving implicit conversions. Given this knowledge,
the fix is straightforward: externs are picked up in a prior pass.
This commit is contained in:
Chris Fallin
2022-04-28 11:16:46 -07:00
committed by GitHub
parent 936f4efd6a
commit 477d394288
2 changed files with 88 additions and 56 deletions

View File

@@ -0,0 +1,22 @@
(type T (primitive T))
(type U (primitive U))
(type V (primitive V))
(convert T U t_to_u)
(type Result (enum (T (u U) (v V))))
;; Use the implicit converter before the underlying constructor is
;; declared (below). Also use one of the conversions before it is
;; declared (below).
(decl entry (T) Result)
(rule (entry t)
(Result.T t t))
(convert T V t_to_v)
(decl t_to_u (T) U)
(extern constructor t_to_u t_to_u)
(decl t_to_v (T) V)
(rule (t_to_v _) 0)

View File

@@ -793,6 +793,8 @@ impl TermEnv {
tyenv.return_errors()?;
env.collect_converters(tyenv, defs);
tyenv.return_errors()?;
env.collect_externs(tyenv, defs);
tyenv.return_errors()?;
env.collect_rules(tyenv, defs);
env.check_for_undefined_decls(tyenv, defs);
env.check_for_expr_terms_without_constructors(tyenv, defs);
@@ -1164,64 +1166,9 @@ impl TermEnv {
}
}
fn collect_rules(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) {
fn collect_externs(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) {
for def in &defs.defs {
match def {
&ast::Def::Rule(ref rule) => {
let pos = rule.pos;
let mut bindings = Bindings {
next_var: 0,
vars: vec![],
};
let rule_term = match rule.pattern.root_term() {
Some(name) => {
let sym = tyenv.intern_mut(name);
match self.term_map.get(&sym) {
Some(term) => *term,
None => {
tyenv.report_error(
pos,
"Cannot define a rule for an unknown term".to_string(),
);
continue;
}
}
}
None => {
tyenv.report_error(
pos,
"Rule does not have a term at the root of its left-hand side"
.to_string(),
);
continue;
}
};
let (lhs, ty) = unwrap_or_continue!(self.translate_pattern(
tyenv,
rule_term,
&rule.pattern,
None,
&mut bindings,
/* is_root = */ true,
));
let rhs = unwrap_or_continue!(self.translate_expr(
tyenv,
&rule.expr,
ty,
&mut bindings
));
let rid = RuleId(self.rules.len());
self.rules.push(Rule {
id: rid,
lhs,
rhs,
prio: rule.prio,
pos,
});
}
&ast::Def::Extern(ast::Extern::Constructor {
ref term,
ref func,
@@ -1356,6 +1303,69 @@ impl TermEnv {
}
}
fn collect_rules(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) {
for def in &defs.defs {
match def {
&ast::Def::Rule(ref rule) => {
let pos = rule.pos;
let mut bindings = Bindings {
next_var: 0,
vars: vec![],
};
let rule_term = match rule.pattern.root_term() {
Some(name) => {
let sym = tyenv.intern_mut(name);
match self.term_map.get(&sym) {
Some(term) => *term,
None => {
tyenv.report_error(
pos,
"Cannot define a rule for an unknown term".to_string(),
);
continue;
}
}
}
None => {
tyenv.report_error(
pos,
"Rule does not have a term at the root of its left-hand side"
.to_string(),
);
continue;
}
};
let (lhs, ty) = unwrap_or_continue!(self.translate_pattern(
tyenv,
rule_term,
&rule.pattern,
None,
&mut bindings,
/* is_root = */ true,
));
let rhs = unwrap_or_continue!(self.translate_expr(
tyenv,
&rule.expr,
ty,
&mut bindings
));
let rid = RuleId(self.rules.len());
self.rules.push(Rule {
id: rid,
lhs,
rhs,
prio: rule.prio,
pos,
});
}
_ => {}
}
}
}
fn check_for_undefined_decls(&self, tyenv: &mut TypeEnv, defs: &ast::Defs) {
for def in &defs.defs {
if let ast::Def::Decl(decl) = def {