Revamped error handling: keep going beyond some errors to accumulate a list of build errors, like most conventional compilers.

This commit is contained in:
Chris Fallin
2021-09-09 23:27:25 -07:00
parent b46fa6acb0
commit 7d38b3b6d8
8 changed files with 359 additions and 308 deletions

View File

@@ -2,15 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
@@ -64,11 +55,7 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
@@ -80,12 +67,6 @@ dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "isle"
version = "0.1.0"
@@ -93,7 +74,6 @@ dependencies = [
"clap",
"env_logger",
"log",
"thiserror",
]
[[package]]
@@ -111,73 +91,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[package]]
name = "proc-macro2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
@@ -187,38 +106,12 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "vec_map"
version = "0.8.2"
@@ -241,15 +134,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View File

@@ -7,6 +7,5 @@ license = "Apache-2.0 WITH LLVM-exception"
[dependencies]
log = "0.4"
env_logger = "0.8"
thiserror = "1.0"
env_logger = { version = "0.8", default-features = false }
clap = "2.33"

View File

@@ -0,0 +1,36 @@
(type u32 (primitive u32))
(type bool (primitive bool))
(type A (enum (A1 (x u32))))
(decl Ext1 (u32) A)
(decl Ext2 (u32) A)
(extern extractor Ext1 ext1)
(extern extractor Ext2 ext2)
(decl C (bool) A)
(extern constructor C c)
(decl Lower (A) A)
(rule
(Lower
(and
a
(Ext1 x)
(Ext2 =q)))
(C y))
(type R (enum (A (x u32))))
(type Opcode (enum A B C))
(type MachInst (enum D E F))
(decl Lower2 (Opcode) MachInst)
(rule
(Lower2 (Opcode.A))
(R.A (Opcode.A)))
(rule
(Lower2 (Opcode.B))
(MachInst.E))
(rule
(Lower2 (Opcode.C))
(MachInst.F))

View File

@@ -3,9 +3,9 @@
use crate::error::Error;
use crate::{ast, codegen, sema};
pub fn compile(defs: &ast::Defs) -> Result<String, Error> {
pub fn compile(defs: &ast::Defs) -> Result<String, Vec<Error>> {
let mut typeenv = sema::TypeEnv::from_ast(defs)?;
let termenv = sema::TermEnv::from_ast(&mut typeenv, defs)?;
let codegen = codegen::Codegen::compile(&typeenv, &termenv)?;
codegen.generate_rust()
let codegen = codegen::Codegen::compile(&typeenv, &termenv).map_err(|e| vec![e])?;
codegen.generate_rust().map_err(|e| vec![e])
}

View File

@@ -1,50 +1,50 @@
//! Error types.
use crate::lexer::Pos;
use thiserror::Error;
use std::fmt;
#[derive(Debug, Error)]
#[derive(Clone, Debug)]
pub enum Error {
#[error("Parse error")]
ParseError(#[from] ParseError),
#[error("Semantic error")]
SemaError(#[from] SemaError),
#[error("IO error")]
IoError(#[from] std::io::Error),
#[error("Formatting error")]
FmtError(#[from] std::fmt::Error),
CompileError {
msg: String,
filename: String,
pos: Pos,
},
SystemError {
msg: String,
},
}
#[derive(Clone, Debug, Error)]
pub struct ParseError {
pub msg: String,
pub filename: String,
pub pos: Pos,
}
#[derive(Clone, Debug, Error)]
pub struct SemaError {
pub msg: String,
pub filename: String,
pub pos: Pos,
}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}:{}:{}: {}",
self.filename, self.pos.line, self.pos.col, self.msg
)
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Error::CompileError {
ref msg,
ref filename,
pos,
} => {
write!(f, "{}:{}: {}", filename, pos.line, msg)
}
&Error::SystemError { ref msg } => {
write!(f, "{}", msg)
}
}
}
}
impl std::fmt::Display for SemaError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}:{}:{}: {}",
self.filename, self.pos.line, self.pos.col, self.msg
)
impl std::error::Error for Error {}
impl std::convert::From<std::fmt::Error> for Error {
fn from(e: std::fmt::Error) -> Error {
Error::SystemError {
msg: format!("{}", e),
}
}
}
impl std::convert::From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Error {
Error::SystemError {
msg: format!("{}", e),
}
}
}

View File

@@ -47,7 +47,16 @@ fn main() -> Result<(), error::Error> {
let lexer = lexer::Lexer::from_files(input_files)?;
let mut parser = parser::Parser::new(lexer);
let defs = parser.parse_defs()?;
let code = compile::compile(&defs)?;
let code = match compile::compile(&defs) {
Ok(code) => code,
Err(errors) => {
for error in errors {
eprintln!("{}", error);
}
eprintln!("Failed to compile.");
std::process::exit(1);
}
};
{
use std::io::Write;

View File

@@ -9,15 +9,15 @@ pub struct Parser<'a> {
lexer: Lexer<'a>,
}
pub type ParseResult<T> = std::result::Result<T, ParseError>;
pub type ParseResult<T> = std::result::Result<T, Error>;
impl<'a> Parser<'a> {
pub fn new(lexer: Lexer<'a>) -> Parser<'a> {
Parser { lexer }
}
pub fn error(&self, pos: Pos, msg: String) -> ParseError {
ParseError {
pub fn error(&self, pos: Pos, msg: String) -> Error {
Error::CompileError {
filename: self.lexer.filenames[pos.file].clone(),
pos,
msg,

View File

@@ -5,7 +5,7 @@ use crate::error::*;
use crate::lexer::Pos;
use std::collections::HashMap;
pub type SemaResult<T> = std::result::Result<T, SemaError>;
pub type SemaResult<T> = std::result::Result<T, Vec<Error>>;
#[macro_export]
macro_rules! declare_id {
@@ -35,6 +35,7 @@ pub struct TypeEnv {
pub sym_map: HashMap<String, Sym>,
pub types: Vec<Type>,
pub type_map: HashMap<Sym, TypeId>,
pub errors: Vec<Error>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -296,6 +297,7 @@ impl TypeEnv {
sym_map: HashMap::new(),
types: vec![],
type_map: HashMap::new(),
errors: vec![],
};
// Traverse defs, assigning type IDs to type names. We'll fill
@@ -306,10 +308,11 @@ impl TypeEnv {
let tid = TypeId(tyenv.type_map.len());
let name = tyenv.intern_mut(&td.name);
if tyenv.type_map.contains_key(&name) {
return Err(tyenv.error(
tyenv.report_error(
td.pos,
format!("Type name defined more than once: '{}'", td.name.0),
));
);
continue;
}
tyenv.type_map.insert(name, tid);
}
@@ -324,7 +327,12 @@ impl TypeEnv {
for def in &defs.defs {
match def {
&ast::Def::Type(ref td) => {
let ty = tyenv.type_from_ast(TypeId(tid), td)?;
let ty = match tyenv.type_from_ast(TypeId(tid), td) {
Some(ty) => ty,
None => {
continue;
}
};
tyenv.types.push(ty);
tid += 1;
}
@@ -332,13 +340,23 @@ impl TypeEnv {
}
}
tyenv.return_errors()?;
Ok(tyenv)
}
fn type_from_ast(&mut self, tid: TypeId, ty: &ast::Type) -> SemaResult<Type> {
fn return_errors(&mut self) -> SemaResult<()> {
if self.errors.len() > 0 {
Err(std::mem::take(&mut self.errors))
} else {
Ok(())
}
}
fn type_from_ast(&mut self, tid: TypeId, ty: &ast::Type) -> Option<Type> {
let name = self.intern(&ty.name).unwrap();
match &ty.ty {
&ast::TypeValue::Primitive(ref id) => Ok(Type::Primitive(tid, self.intern_mut(id))),
&ast::TypeValue::Primitive(ref id) => Some(Type::Primitive(tid, self.intern_mut(id))),
&ast::TypeValue::Enum(ref ty_variants) => {
let mut variants = vec![];
for variant in ty_variants {
@@ -347,34 +365,37 @@ impl TypeEnv {
let name = self.intern_mut(&variant.name);
let id = VariantId(variants.len());
if variants.iter().any(|v: &Variant| v.name == name) {
return Err(self.error(
self.report_error(
ty.pos,
format!("Duplicate variant name in type: '{}'", variant.name.0),
));
);
return None;
}
let mut fields = vec![];
for field in &variant.fields {
let field_name = self.intern_mut(&field.name);
if fields.iter().any(|f: &Field| f.name == field_name) {
return Err(self.error(
self.report_error(
ty.pos,
format!(
"Duplicate field name '{}' in variant '{}' of type",
field.name.0, variant.name.0
),
));
);
return None;
}
let field_ty = self.intern_mut(&field.ty);
let field_tid = match self.type_map.get(&field_ty) {
Some(tid) => *tid,
None => {
return Err(self.error(
self.report_error(
ty.pos,
format!(
"Unknown type '{}' for field '{}' in variant '{}'",
field.ty.0, field.name.0, variant.name.0
),
));
);
return None;
}
};
fields.push(Field {
@@ -390,7 +411,7 @@ impl TypeEnv {
fields,
});
}
Ok(Type::Enum {
Some(Type::Enum {
name,
id: tid,
is_extern: ty.is_extern,
@@ -401,14 +422,19 @@ impl TypeEnv {
}
}
fn error(&self, pos: Pos, msg: String) -> SemaError {
SemaError {
fn error(&self, pos: Pos, msg: String) -> Error {
Error::CompileError {
filename: self.filenames[pos.file].clone(),
pos,
msg,
}
}
fn report_error(&mut self, pos: Pos, msg: String) {
let err = self.error(pos, msg);
self.errors.push(err);
}
pub fn intern_mut(&mut self, ident: &ast::Ident) -> Sym {
if let Some(s) = self.sym_map.get(&ident.0).cloned() {
s
@@ -446,24 +472,27 @@ impl TermEnv {
rules: vec![],
};
env.collect_term_sigs(tyenv, defs)?;
env.collect_enum_variant_terms(tyenv)?;
env.collect_constructors(tyenv, defs)?;
env.collect_extractor_templates(tyenv, defs)?;
env.collect_rules(tyenv, defs)?;
env.collect_term_sigs(tyenv, defs);
env.collect_enum_variant_terms(tyenv);
env.collect_constructors(tyenv, defs);
env.collect_extractor_templates(tyenv, defs);
tyenv.return_errors()?;
env.collect_rules(tyenv, defs);
tyenv.return_errors()?;
Ok(env)
}
fn collect_term_sigs(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) -> SemaResult<()> {
fn collect_term_sigs(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) {
for def in &defs.defs {
match def {
&ast::Def::Decl(ref decl) => {
let tid = TermId(self.terms.len());
let name = tyenv.intern_mut(&decl.term);
if self.term_map.contains_key(&name) {
return Err(
tyenv.error(decl.pos, format!("Duplicate decl for '{}'", decl.term.0))
tyenv.report_error(
decl.pos,
format!("Duplicate decl for '{}'", decl.term.0),
);
}
self.term_map.insert(name, tid);
@@ -474,18 +503,32 @@ impl TermEnv {
.map(|id| {
let sym = tyenv.intern_mut(id);
tyenv.type_map.get(&sym).cloned().ok_or_else(|| {
tyenv.error(decl.pos, format!("Unknown arg type: '{}'", id.0))
tyenv.report_error(
decl.pos,
format!("Unknown arg type: '{}'", id.0),
);
()
})
})
.collect::<SemaResult<Vec<TypeId>>>()?;
.collect::<Result<Vec<TypeId>, ()>>();
let arg_tys = match arg_tys {
Ok(a) => a,
Err(_) => {
continue;
}
};
let ret_ty = {
let sym = tyenv.intern_mut(&decl.ret_ty);
tyenv.type_map.get(&sym).cloned().ok_or_else(|| {
tyenv.error(
decl.pos,
format!("Unknown return type: '{}'", decl.ret_ty.0),
)
})?
match tyenv.type_map.get(&sym).cloned() {
Some(t) => t,
None => {
tyenv.report_error(
decl.pos,
format!("Unknown return type: '{}'", decl.ret_ty.0),
);
continue;
}
}
};
self.terms.push(Term {
@@ -499,12 +542,11 @@ impl TermEnv {
_ => {}
}
}
Ok(())
}
fn collect_enum_variant_terms(&mut self, tyenv: &mut TypeEnv) -> SemaResult<()> {
for ty in &tyenv.types {
fn collect_enum_variant_terms(&mut self, tyenv: &mut TypeEnv) {
'types: for i in 0..tyenv.types.len() {
let ty = &tyenv.types[i];
match ty {
&Type::Enum {
pos,
@@ -514,13 +556,12 @@ impl TermEnv {
} => {
for variant in variants {
if self.term_map.contains_key(&variant.fullname) {
return Err(tyenv.error(
let variant_name = tyenv.syms[variant.fullname.index()].clone();
tyenv.report_error(
pos,
format!(
"Duplicate enum variant constructor: '{}'",
tyenv.syms[variant.fullname.index()]
),
));
format!("Duplicate enum variant constructor: '{}'", variant_name,),
);
continue 'types;
}
let tid = TermId(self.terms.len());
let arg_tys = variant.fields.iter().map(|fld| fld.ty).collect::<Vec<_>>();
@@ -540,11 +581,9 @@ impl TermEnv {
_ => {}
}
}
Ok(())
}
fn collect_constructors(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) -> SemaResult<()> {
fn collect_constructors(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) {
for def in &defs.defs {
match def {
&ast::Def::Rule(ref rule) => {
@@ -552,19 +591,20 @@ impl TermEnv {
let term = match rule.pattern.root_term() {
Some(t) => t,
None => {
return Err(tyenv.error(
tyenv.report_error(
pos,
"Rule does not have a term at the LHS root".to_string(),
));
);
continue;
}
};
let sym = tyenv.intern_mut(&term);
let term = match self.term_map.get(&sym) {
Some(&tid) => tid,
None => {
return Err(
tyenv.error(pos, "Rule LHS root term is not defined".to_string())
);
tyenv
.report_error(pos, "Rule LHS root term is not defined".to_string());
continue;
}
};
let termdata = &mut self.terms[term.index()];
@@ -576,31 +616,32 @@ impl TermEnv {
// OK, no error; multiple rules can apply to one internal constructor term.
}
_ => {
return Err(tyenv.error(pos, "Rule LHS root term is incorrect kind; cannot be internal constructor".to_string()));
tyenv.report_error(pos, "Rule LHS root term is incorrect kind; cannot be internal constructor".to_string());
continue;
}
}
}
_ => {}
}
}
Ok(())
}
fn collect_extractor_templates(
&mut self,
tyenv: &mut TypeEnv,
defs: &ast::Defs,
) -> SemaResult<()> {
fn collect_extractor_templates(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) {
for def in &defs.defs {
match def {
&ast::Def::Extractor(ref ext) => {
let sym = tyenv.intern_mut(&ext.term);
let term = self.term_map.get(&sym).ok_or_else(|| {
tyenv.error(
ext.pos,
"Extractor macro body definition on a non-existent term".to_string(),
)
})?;
let term = match self.term_map.get(&sym) {
Some(x) => x,
None => {
tyenv.report_error(
ext.pos,
"Extractor macro body definition on a non-existent term"
.to_string(),
);
return;
}
};
let termdata = &mut self.terms[term.index()];
let template = ext.template.make_macro_template(&ext.args[..]);
log::trace!("extractor def: {:?} becomes template {:?}", def, template);
@@ -609,22 +650,21 @@ impl TermEnv {
termdata.kind = TermKind::InternalExtractor { template };
}
_ => {
return Err(tyenv.error(
tyenv.report_error(
ext.pos,
"Extractor macro body defined on term of incorrect kind"
.to_string(),
));
);
continue;
}
}
}
_ => {}
}
}
Ok(())
}
fn collect_rules(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) -> SemaResult<()> {
fn collect_rules(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) {
for def in &defs.defs {
match def {
&ast::Def::Rule(ref rule) => {
@@ -634,15 +674,26 @@ impl TermEnv {
vars: vec![],
};
let (lhs, ty) = self.translate_pattern(
let (lhs, ty) = match self.translate_pattern(
tyenv,
rule.pos,
&rule.pattern,
None,
&mut bindings,
)?;
) {
Some(x) => x,
None => {
// Keep going to collect more errors.
continue;
}
};
let rhs =
self.translate_expr(tyenv, rule.pos, &rule.expr, ty, &mut bindings)?;
match self.translate_expr(tyenv, rule.pos, &rule.expr, ty, &mut bindings) {
Some(x) => x,
None => {
continue;
}
};
let rid = RuleId(self.rules.len());
self.rules.push(Rule {
@@ -663,10 +714,11 @@ impl TermEnv {
let term_id = match self.term_map.get(&term_sym) {
Some(term) => term,
None => {
return Err(tyenv.error(
tyenv.report_error(
pos,
format!("Constructor declared on undefined term '{}'", term.0),
))
);
continue;
}
};
let termdata = &mut self.terms[term_id.index()];
@@ -675,13 +727,13 @@ impl TermEnv {
termdata.kind = TermKind::ExternalConstructor { name: func_sym };
}
_ => {
return Err(tyenv.error(
tyenv.report_error(
pos,
format!(
"Constructor defined on term of improper type '{}'",
term.0
),
));
);
}
}
}
@@ -697,10 +749,11 @@ impl TermEnv {
let term_id = match self.term_map.get(&term_sym) {
Some(term) => term,
None => {
return Err(tyenv.error(
tyenv.report_error(
pos,
format!("Extractor declared on undefined term '{}'", term.0),
))
);
continue;
}
};
@@ -708,7 +761,8 @@ impl TermEnv {
let arg_polarity = if let Some(pol) = arg_polarity.as_ref() {
if pol.len() != termdata.arg_tys.len() {
return Err(tyenv.error(pos, "Incorrect number of argument-polarity directions in extractor definition".to_string()));
tyenv.report_error(pos, "Incorrect number of argument-polarity directions in extractor definition".to_string());
continue;
}
pol.clone()
} else {
@@ -724,18 +778,17 @@ impl TermEnv {
};
}
_ => {
return Err(tyenv.error(
tyenv.report_error(
pos,
format!("Extractor defined on term of improper type '{}'", term.0),
));
);
continue;
}
}
}
_ => {}
}
}
Ok(())
}
fn translate_pattern(
@@ -745,37 +798,55 @@ impl TermEnv {
pat: &ast::Pattern,
expected_ty: Option<TypeId>,
bindings: &mut Bindings,
) -> SemaResult<(Pattern, TypeId)> {
) -> Option<(Pattern, TypeId)> {
log::trace!("translate_pattern: {:?}", pat);
log::trace!("translate_pattern: bindings = {:?}", bindings);
match pat {
// TODO: flag on primitive type decl indicating it's an integer type?
&ast::Pattern::ConstInt { val } => {
let ty = expected_ty.ok_or_else(|| {
tyenv.error(pos, "Need an implied type for an integer constant".into())
})?;
Ok((Pattern::ConstInt(ty, val), ty))
let ty = match expected_ty {
Some(t) => t,
None => {
tyenv.report_error(
pos,
"Need an implied type for an integer constant".into(),
);
return None;
}
};
Some((Pattern::ConstInt(ty, val), ty))
}
&ast::Pattern::Wildcard => {
let ty = expected_ty.ok_or_else(|| {
tyenv.error(pos, "Need an implied type for a wildcard".into())
})?;
Ok((Pattern::Wildcard(ty), ty))
let ty = match expected_ty {
Some(t) => t,
None => {
tyenv.report_error(pos, "Need an implied type for a wildcard".into());
return None;
}
};
Some((Pattern::Wildcard(ty), ty))
}
&ast::Pattern::And { ref subpats } => {
let mut expected_ty = expected_ty;
let mut children = vec![];
for subpat in subpats {
let (subpat, ty) =
self.translate_pattern(tyenv, pos, &*subpat, expected_ty, bindings)?;
match self.translate_pattern(tyenv, pos, &*subpat, expected_ty, bindings) {
Some(x) => x,
None => {
// Try to keep going for more errors.
continue;
}
};
expected_ty = expected_ty.or(Some(ty));
children.push(subpat);
}
if expected_ty.is_none() {
return Err(tyenv.error(pos, "No type for (and ...) form.".to_string()));
tyenv.report_error(pos, "No type for (and ...) form.".to_string());
return None;
}
let ty = expected_ty.unwrap();
Ok((Pattern::And(ty, children), ty))
Some((Pattern::And(ty, children), ty))
}
&ast::Pattern::BindPattern {
ref var,
@@ -787,30 +858,32 @@ impl TermEnv {
let name = tyenv.intern_mut(var);
if bindings.vars.iter().any(|bv| bv.name == name) {
return Err(tyenv.error(
tyenv.report_error(
pos,
format!("Rebound variable name in LHS pattern: '{}'", var.0),
));
format!("Re-bound variable name in LHS pattern: '{}'", var.0),
);
// Try to keep going.
}
let id = VarId(bindings.next_var);
bindings.next_var += 1;
log::trace!("binding var {:?}", var.0);
bindings.vars.push(BoundVar { name, id, ty });
Ok((Pattern::BindPattern(ty, id, Box::new(subpat)), ty))
Some((Pattern::BindPattern(ty, id, Box::new(subpat)), ty))
}
&ast::Pattern::Var { ref var } => {
// Look up the variable; it must already have been bound.
let name = tyenv.intern_mut(var);
let bv = match bindings.vars.iter().rev().find(|bv| bv.name == name) {
None => {
return Err(tyenv.error(
tyenv.report_error(
pos,
format!(
"Unknown variable '{}' in bound-var pattern '={}'",
var.0, var.0
),
))
);
return None;
}
Some(bv) => bv,
};
@@ -818,17 +891,28 @@ impl TermEnv {
None => bv.ty,
Some(expected_ty) if expected_ty == bv.ty => bv.ty,
Some(expected_ty) => {
return Err(tyenv.error(pos, format!("Mismatched types: pattern expects type '{}' but already-bound var '{}' has type '{}'", tyenv.types[expected_ty.index()].name(tyenv), var.0, tyenv.types[bv.ty.index()].name(tyenv))));
tyenv.report_error(
pos,
format!(
"Mismatched types: pattern expects type '{}' but already-bound var '{}' has type '{}'",
tyenv.types[expected_ty.index()].name(tyenv),
var.0,
tyenv.types[bv.ty.index()].name(tyenv)));
bv.ty // Try to keep going for more errors.
}
};
Ok((Pattern::Var(ty, bv.id), ty))
Some((Pattern::Var(ty, bv.id), ty))
}
&ast::Pattern::Term { ref sym, ref args } => {
let name = tyenv.intern_mut(&sym);
// Look up the term.
let tid = self.term_map.get(&name).ok_or_else(|| {
tyenv.error(pos, format!("Unknown term in pattern: '{}'", sym.0))
})?;
let tid = match self.term_map.get(&name) {
Some(t) => t,
None => {
tyenv.report_error(pos, format!("Unknown term in pattern: '{}'", sym.0));
return None;
}
};
// Get the return type and arg types. Verify the
// expected type of this pattern, if any, against the
@@ -838,13 +922,19 @@ impl TermEnv {
None => ret_ty,
Some(expected_ty) if expected_ty == ret_ty => ret_ty,
Some(expected_ty) => {
return Err(tyenv.error(pos, format!("Mismatched types: pattern expects type '{}' but term has return type '{}'", tyenv.types[expected_ty.index()].name(tyenv), tyenv.types[ret_ty.index()].name(tyenv))));
tyenv.report_error(
pos,
format!(
"Mismatched types: pattern expects type '{}' but term has return type '{}'",
tyenv.types[expected_ty.index()].name(tyenv),
tyenv.types[ret_ty.index()].name(tyenv)));
ret_ty // Try to keep going for more errors.
}
};
// Check that we have the correct argument count.
if self.terms[tid.index()].arg_tys.len() != args.len() {
return Err(tyenv.error(
tyenv.report_error(
pos,
format!(
"Incorrect argument count for term '{}': got {}, expect {}",
@@ -852,7 +942,7 @@ impl TermEnv {
args.len(),
self.terms[tid.index()].arg_tys.len()
),
));
);
}
let termdata = &self.terms[tid.index()];
@@ -861,7 +951,7 @@ impl TermEnv {
&TermKind::EnumVariant { .. } => {
for arg in args {
if let &ast::TermArgPattern::Expr(..) = arg {
return Err(tyenv.error(pos, format!("Term in pattern '{}' cannot have an injected expr, because it is an enum variant", sym.0)));
tyenv.report_error(pos, format!("Term in pattern '{}' cannot have an injected expr, because it is an enum variant", sym.0));
}
}
}
@@ -872,15 +962,15 @@ impl TermEnv {
match (arg, pol) {
(&ast::TermArgPattern::Expr(..), &ArgPolarity::Input) => {}
(&ast::TermArgPattern::Expr(..), &ArgPolarity::Output) => {
return Err(tyenv.error(
tyenv.report_error(
pos,
"Expression used for output-polarity extractor arg"
.to_string(),
));
);
}
(_, &ArgPolarity::Output) => {}
(_, &ArgPolarity::Input) => {
return Err(tyenv.error(pos, "Non-expression used in pattern but expression required for input-polarity extractor arg".to_string()));
tyenv.report_error(pos, "Non-expression used in pattern but expression required for input-polarity extractor arg".to_string());
}
}
}
@@ -895,7 +985,8 @@ impl TermEnv {
let sub_ast = match template_arg {
&ast::TermArgPattern::Pattern(ref pat) => pat.clone(),
&ast::TermArgPattern::Expr(_) => {
return Err(tyenv.error(pos, "Cannot expand an extractor macro with an expression in a macro argument".to_string()));
tyenv.report_error(pos, "Cannot expand an extractor macro with an expression in a macro argument".to_string());
return None;
}
};
macro_args.push(sub_ast.clone());
@@ -908,8 +999,10 @@ impl TermEnv {
// OK.
}
&TermKind::Declared => {
return Err(tyenv
.error(pos, format!("Declared but undefined term '{}' used", sym.0)));
tyenv.report_error(
pos,
format!("Declared but undefined term '{}' used", sym.0),
);
}
}
@@ -917,12 +1010,23 @@ impl TermEnv {
let mut subpats = vec![];
for (i, arg) in args.iter().enumerate() {
let arg_ty = self.terms[tid.index()].arg_tys[i];
let (subpat, _) =
self.translate_pattern_term_arg(tyenv, pos, arg, Some(arg_ty), bindings)?;
let (subpat, _) = match self.translate_pattern_term_arg(
tyenv,
pos,
arg,
Some(arg_ty),
bindings,
) {
Some(x) => x,
None => {
// Try to keep going for more errors.
continue;
}
};
subpats.push(subpat);
}
Ok((Pattern::Term(ty, *tid, subpats), ty))
Some((Pattern::Term(ty, *tid, subpats), ty))
}
&ast::Pattern::MacroArg { .. } => unreachable!(),
}
@@ -935,23 +1039,24 @@ impl TermEnv {
pat: &ast::TermArgPattern,
expected_ty: Option<TypeId>,
bindings: &mut Bindings,
) -> SemaResult<(TermArgPattern, TypeId)> {
) -> Option<(TermArgPattern, TypeId)> {
match pat {
&ast::TermArgPattern::Pattern(ref pat) => {
let (subpat, ty) =
self.translate_pattern(tyenv, pos, pat, expected_ty, bindings)?;
Ok((TermArgPattern::Pattern(subpat), ty))
Some((TermArgPattern::Pattern(subpat), ty))
}
&ast::TermArgPattern::Expr(ref expr) => {
if expected_ty.is_none() {
return Err(tyenv.error(
tyenv.report_error(
pos,
"Expression in pattern must have expected type".to_string(),
));
);
return None;
}
let ty = expected_ty.unwrap();
let expr = self.translate_expr(tyenv, pos, expr, expected_ty.unwrap(), bindings)?;
Ok((TermArgPattern::Expr(expr), ty))
Some((TermArgPattern::Expr(expr), ty))
}
}
}
@@ -963,28 +1068,32 @@ impl TermEnv {
expr: &ast::Expr,
ty: TypeId,
bindings: &mut Bindings,
) -> SemaResult<Expr> {
) -> Option<Expr> {
log::trace!("translate_expr: {:?}", expr);
match expr {
&ast::Expr::Term { ref sym, ref args } => {
// Look up the term.
let name = tyenv.intern_mut(&sym);
// Look up the term.
let tid = self.term_map.get(&name).ok_or_else(|| {
tyenv.error(pos, format!("Unknown term in pattern: '{}'", sym.0))
})?;
let tid = match self.term_map.get(&name) {
Some(t) => t,
None => {
tyenv.report_error(pos, format!("Unknown term in pattern: '{}'", sym.0));
return None;
}
};
// Get the return type and arg types. Verify the
// expected type of this pattern, if any, against the
// return type of the term.
let ret_ty = self.terms[tid.index()].ret_ty;
if ret_ty != ty {
return Err(tyenv.error(pos, format!("Mismatched types: expression expects type '{}' but term has return type '{}'", tyenv.types[ty.index()].name(tyenv), tyenv.types[ret_ty.index()].name(tyenv))));
tyenv.report_error(pos, format!("Mismatched types: expression expects type '{}' but term has return type '{}'", tyenv.types[ty.index()].name(tyenv), tyenv.types[ret_ty.index()].name(tyenv)));
}
// Check that we have the correct argument count.
if self.terms[tid.index()].arg_tys.len() != args.len() {
return Err(tyenv.error(
tyenv.report_error(
pos,
format!(
"Incorrect argument count for term '{}': got {}, expect {}",
@@ -992,32 +1101,38 @@ impl TermEnv {
args.len(),
self.terms[tid.index()].arg_tys.len()
),
));
);
}
// Resolve subexpressions.
let mut subexprs = vec![];
for (i, arg) in args.iter().enumerate() {
let arg_ty = self.terms[tid.index()].arg_tys[i];
let subexpr = self.translate_expr(tyenv, pos, arg, arg_ty, bindings)?;
let subexpr = match self.translate_expr(tyenv, pos, arg, arg_ty, bindings) {
Some(s) => s,
None => {
continue;
}
};
subexprs.push(subexpr);
}
Ok(Expr::Term(ty, *tid, subexprs))
Some(Expr::Term(ty, *tid, subexprs))
}
&ast::Expr::Var { ref name } => {
let sym = tyenv.intern_mut(name);
// Look through bindings, innermost (most recent) first.
let bv = match bindings.vars.iter().rev().find(|b| b.name == sym) {
None => {
return Err(tyenv.error(pos, format!("Unknown variable '{}'", name.0)));
tyenv.report_error(pos, format!("Unknown variable '{}'", name.0));
return None;
}
Some(bv) => bv,
};
// Verify type.
if bv.ty != ty {
return Err(tyenv.error(
tyenv.report_error(
pos,
format!(
"Variable '{}' has type {} but we need {} in context",
@@ -1025,12 +1140,12 @@ impl TermEnv {
tyenv.types[bv.ty.index()].name(tyenv),
tyenv.types[ty.index()].name(tyenv)
),
));
);
}
Ok(Expr::Var(bv.ty, bv.id))
Some(Expr::Var(bv.ty, bv.id))
}
&ast::Expr::ConstInt { val } => Ok(Expr::ConstInt(ty, val)),
&ast::Expr::ConstInt { val } => Some(Expr::ConstInt(ty, val)),
&ast::Expr::Let { ref defs, ref body } => {
let orig_binding_len = bindings.vars.len();
@@ -1040,33 +1155,41 @@ impl TermEnv {
// Check that the given variable name does not already exist.
let name = tyenv.intern_mut(&def.var);
if bindings.vars.iter().any(|bv| bv.name == name) {
return Err(
tyenv.error(pos, format!("Variable '{}' already bound", def.var.0))
);
tyenv.report_error(pos, format!("Variable '{}' already bound", def.var.0));
}
// Look up the type.
let tysym = match tyenv.intern(&def.ty) {
Some(ty) => ty,
None => {
return Err(tyenv.error(
tyenv.report_error(
pos,
format!("Unknown type {} for variable '{}'", def.ty.0, def.var.0),
))
);
continue;
}
};
let tid = match tyenv.type_map.get(&tysym) {
Some(tid) => *tid,
None => {
return Err(tyenv.error(
tyenv.report_error(
pos,
format!("Unknown type {} for variable '{}'", def.ty.0, def.var.0),
))
);
continue;
}
};
// Evaluate the variable's value.
let val = Box::new(self.translate_expr(tyenv, pos, &def.val, ty, bindings)?);
let val = Box::new(
match self.translate_expr(tyenv, pos, &def.val, ty, bindings) {
Some(e) => e,
None => {
// Keep going for more errors.
continue;
}
},
);
// Bind the var with the given type.
let id = VarId(bindings.next_var);
@@ -1083,7 +1206,7 @@ impl TermEnv {
// Pop the bindings.
bindings.vars.truncate(orig_binding_len);
Ok(Expr::Let(body_ty, let_defs, body))
Some(Expr::Let(body_ty, let_defs, body))
}
}
}