Skip to content

Commit beb02ef

Browse files
fpham0701ekiwi
andauthored
Type Checking, Error Handling (#4)
* start adding type info for the dut * Began Type-Checking Serialization Implementation -- IR -- - encapsulated more structs - Updated type_args transcation field and Struct Type - Added add_with_parent to get parent type of symbol -- Serialize -- - serialized type_args - abstracted away DUT types to now Struct types - Need to implement Structs serialization soon * Implementation of serialization for structs - Added ability to serialize structs prior to src code - Refactored code, added helper functions in IR * Updated Parent Type Inference - Adding a symbol with a parent will try to name-match the type (according to the parent's struct type) - If no match is found, will resolve in Unknown type * Started Type Checking Functionality - Added function to get exprs_id in IR - expr type checking, evaluates each expression and ensures type - currently panics with incorrect type check, TODO: use rust lib to print error message - TODO: stmt type checking * Continued Type Checking (stmts) - Added is_equivalent method under type Type for easy checking - Added stmt ids getter function for Transaction - Implemented stmt type checking, serializing now type checks - Changed some var names - TODO: Error printing * cargo fmt * Began working on error diagnostics (codespan) - added new metadata field for stmt_ids (under Transactions) to hold corresponding line and column number of error - Began working on implementing codespan in diganostics.rs * messing around with codespan - trying to figure out how to use codespan - basic error warning example made - still need to organize how to detect error (+/warning?) and manage it - updated metadata for expr ids for now (possibly will add stmts) - correctly added diagnostic.rs to lib * Implementation of Codespan for ExprId - Fixed metadata vals (start, end) bytes - Refactored and implemented codespan-use diagnostic.rs file - includes different levels of severity (Error, Warning) - Requires start/end chars of a given code file, with a message and severity - TODO: integrate with serialize and test more with given code, + integrate with ir.rs for smooth use (i.e. succint adding of elements) * Added statements implementation for diagnostics - Added ability for statement diagnostics - Added secondary map for statements in IR with correspodning getters/adders - Created 1 basic test case to test error handling * Integrating diagnostics to serialize - (wip) emitting diagnositcs in type check errors - refactored function headers, signatures, and code - updated calyx_go_done test case to reflect and test new error handling - cleaned up add test case src code * Continued to integrate type checking into serialization Added secondary meta data maps for stmts and expressions, an getters/setters Handled all panics when serializing with type check, refactored some code accordingly Added new hashmap to keep track of all errors so no dupes are shown Created ErrorKey to add to error hashmap Added calyx_go_done test case + updated src code * Refactored type check files - put all type checking files into own file - abstracted type check from serialization - refactored serialize functions accordingly - updated test cases - ir.rs Type implementation adjusted (TODO: type inference) * Updated test cases - needed to add Struct test cases due to parser function not supporting structs and function types - TODO: create a function to generate general test cases for different purposes * rust version update * Updated variable names for expr/stmt locations * added comment for clarity for reported errors --------- Co-authored-by: Kevin Läufer <laeufer@cornell.edu>
1 parent 7f3c2fd commit beb02ef

File tree

11 files changed

+952
-84
lines changed

11 files changed

+952
-84
lines changed

Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
name = "protocols"
33
version = "0.1.0"
44
edition = "2021"
5-
rust-version = "1.73.0"
5+
rust-version = "1.80.1"
66

77
[dependencies]
8-
baa = "0.14.6"
9-
cranelift-entity = "0.111.2"
8+
baa = "0.15.0"
9+
cranelift-entity = "0.114.0"
1010
pest = "2.7.14"
1111
pest_derive = "2.7.14"
1212
rustc-hash = "2.1.0"
13+
thiserror = "2.0.8"
14+
codespan-reporting = "0.11.1"

src/diagnostic.rs

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
// Copyright 2024 Cornell University
2+
// released under MIT License
3+
// author: Nikil Shyamunder <nikil.shyamsunder@gmail.com>
4+
// author: Kevin Laeufer <laeufer@cornell.edu>
5+
// author: Francis Pham <fdp25@cornell.edu>
6+
7+
use std::{collections::HashSet, io::Write};
8+
9+
use codespan_reporting::diagnostic::{
10+
Diagnostic as CodespanDiagnostic, Label as CodespanLabel, LabelStyle, Severity,
11+
};
12+
use codespan_reporting::files::SimpleFiles;
13+
use codespan_reporting::term;
14+
use codespan_reporting::term::termcolor::{Buffer, Color, ColorSpec, WriteColor};
15+
16+
use crate::ir::*;
17+
18+
/// Track Errors
19+
#[derive(Hash, Eq, PartialEq, Debug)]
20+
enum ErrorKey {
21+
ExprKey(ExprId),
22+
StmtKey(StmtId),
23+
}
24+
25+
/// Severity of diagnostic
26+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27+
pub enum Level {
28+
Error,
29+
Warning,
30+
}
31+
32+
/// A label representing a part of the source code
33+
#[derive(Debug, Clone, PartialEq, Eq)]
34+
struct Label {
35+
message: Option<String>,
36+
range: (usize, usize),
37+
}
38+
39+
impl Label {
40+
fn to_codespan_label(&self, fileid: usize) -> CodespanLabel<usize> {
41+
CodespanLabel::new(LabelStyle::Primary, fileid, self.range.0..self.range.1)
42+
.with_message(self.message.clone().unwrap_or_default())
43+
// error msg
44+
}
45+
}
46+
47+
/// Diagnostic of a particular part of source code
48+
struct Diagnostic {
49+
title: String,
50+
message: String,
51+
level: Level,
52+
location: Option<(usize, Label)>,
53+
}
54+
55+
impl Diagnostic {
56+
pub fn emit(&self, buffer: &mut Buffer, files: &SimpleFiles<String, String>) {
57+
if let Some((fileid, label)) = &self.location {
58+
let severity = match self.level {
59+
Level::Error => Severity::Error,
60+
Level::Warning => Severity::Warning,
61+
};
62+
63+
let diagnostic = CodespanDiagnostic::new(severity)
64+
.with_message(&self.message) // Severity: (msg)
65+
.with_labels(vec![label.to_codespan_label(*fileid)]);
66+
67+
let config = term::Config::default(); // Change config depending on how error needs to be produced
68+
term::emit(buffer, &config, files, &diagnostic).expect("Failed to write diagnostic");
69+
} else {
70+
let color = match self.level {
71+
Level::Error => Color::Red,
72+
Level::Warning => Color::Yellow,
73+
};
74+
75+
buffer
76+
.set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)))
77+
.expect("Failed to set color");
78+
writeln!(buffer, "{}", self.title).expect("Failed to write title");
79+
80+
buffer
81+
.set_color(&ColorSpec::new())
82+
.expect("Failed to reset color");
83+
}
84+
}
85+
}
86+
87+
pub struct DiagnosticHandler {
88+
files: SimpleFiles<String, String>,
89+
reported_errs: HashSet<ErrorKey>,
90+
}
91+
92+
impl DiagnosticHandler {
93+
pub fn new() -> Self {
94+
Self {
95+
files: SimpleFiles::new(),
96+
reported_errs: HashSet::new(),
97+
}
98+
}
99+
100+
pub fn add_file(&mut self, name: String, content: String) -> usize {
101+
self.files.add(name, content)
102+
}
103+
104+
pub fn emit_diagnostic_expr(
105+
&mut self,
106+
tr: &Transaction,
107+
expr_id: &ExprId,
108+
message: &str,
109+
level: Level,
110+
) {
111+
// need to check errors to avoid recursive duplication of error message
112+
if !self.reported_errs.insert(ErrorKey::ExprKey(*expr_id)) {
113+
return;
114+
}
115+
let buffer = &mut Buffer::ansi();
116+
if let Some((start, end, fileid)) = tr.get_expr_loc(*expr_id) {
117+
let label = Label {
118+
message: Some(message.to_string()),
119+
range: (start, end),
120+
};
121+
122+
let diagnostic = Diagnostic {
123+
title: format!("{:?} in file {}", level, fileid),
124+
message: message.to_string(),
125+
level,
126+
location: Some((fileid, label)),
127+
};
128+
129+
diagnostic.emit(buffer, &self.files);
130+
131+
print!("{}", String::from_utf8_lossy(buffer.as_slice()));
132+
}
133+
}
134+
135+
pub fn emit_diagnostic_stmt(
136+
&mut self,
137+
tr: &Transaction,
138+
stmt_id: &StmtId,
139+
message: &str,
140+
level: Level,
141+
) {
142+
// need to check errors to avoid recursive duplication of error message
143+
if !self.reported_errs.insert(ErrorKey::StmtKey(*stmt_id)) {
144+
return;
145+
}
146+
let buffer = &mut Buffer::ansi();
147+
if let Some((start, end, fileid)) = tr.get_stmt_loc(*stmt_id) {
148+
let label = Label {
149+
message: Some(message.to_string()),
150+
range: (start, end),
151+
};
152+
153+
let diagnostic = Diagnostic {
154+
title: format!("{:?} in file {}", level, fileid),
155+
message: message.to_string(),
156+
level,
157+
location: Some((fileid, label)),
158+
};
159+
160+
diagnostic.emit(buffer, &self.files);
161+
162+
print!("{}", String::from_utf8_lossy(buffer.as_slice()));
163+
}
164+
}
165+
}
166+
167+
#[cfg(test)]
168+
mod tests {
169+
use crate::typecheck::*;
170+
171+
use super::*;
172+
use baa::BitVecValue;
173+
174+
#[test]
175+
fn test_emit_diagnostic() {
176+
let mut symbols = SymbolTable::default();
177+
let a = symbols.add_without_parent("a".to_string(), Type::BitVec(32));
178+
let b = symbols.add_without_parent("b".to_string(), Type::BitVec(32));
179+
180+
let mut tr = Transaction::new("test_transaction".to_string());
181+
let one_expr = tr.e(Expr::Const(BitVecValue::from_u64(1, 1)));
182+
let zero_expr = tr.e(Expr::Const(BitVecValue::from_u64(0, 1)));
183+
tr.s(Stmt::Assign(a, one_expr));
184+
tr.s(Stmt::Assign(b, zero_expr));
185+
186+
let mut handler = DiagnosticHandler::new();
187+
let file_id = handler.add_file(
188+
"main.calyx".to_string(),
189+
"12345678\nassert_eq!(x, 20);\n".to_string(),
190+
);
191+
192+
tr.add_expr_loc(one_expr, 9, 11, file_id);
193+
tr.add_expr_loc(zero_expr, 12, 15, file_id);
194+
195+
handler.emit_diagnostic_expr(&tr, &one_expr, "Random Warning", Level::Warning);
196+
handler.emit_diagnostic_expr(&tr, &zero_expr, "Random Error", Level::Error);
197+
}
198+
199+
#[test]
200+
fn serialize_calyx_go_down_transaction() {
201+
// Manually create the expected result of parsing `calyx_go_down`.
202+
// Note that the order in which things are created will be different in the parser.
203+
204+
// 1) declare symbols
205+
let mut symbols = SymbolTable::default();
206+
let mut handler: DiagnosticHandler = DiagnosticHandler::new();
207+
let ii = symbols.add_without_parent("ii".to_string(), Type::BitVec(32));
208+
let oo = symbols.add_without_parent("oo".to_string(), Type::BitVec(32));
209+
assert_eq!(symbols["oo"], symbols[oo]);
210+
211+
// declare Calyx struct
212+
let dut_struct = symbols.add_struct(
213+
"Calyx".to_string(),
214+
vec![
215+
Field::new("ii".to_string(), Dir::In, Type::BitVec(32)),
216+
Field::new("go".to_string(), Dir::In, Type::BitVec(1)),
217+
Field::new("done".to_string(), Dir::Out, Type::BitVec(1)),
218+
Field::new("oo".to_string(), Dir::Out, Type::BitVec(32)),
219+
],
220+
);
221+
222+
let dut = symbols.add_without_parent("dut".to_string(), Type::Struct(dut_struct));
223+
let dut_ii = symbols.add_with_parent("ii".to_string(), dut);
224+
let dut_go = symbols.add_with_parent("go".to_string(), dut);
225+
let dut_done = symbols.add_with_parent("done".to_string(), dut);
226+
let dut_oo = symbols.add_with_parent("oo".to_string(), dut);
227+
assert_eq!(symbols["dut.oo"], symbols[dut_oo]);
228+
assert_eq!(symbols["oo"], symbols[oo]);
229+
230+
// create fileid and read file
231+
let input =
232+
std::fs::read_to_string("tests/calyx_go_doneStruct.prot").expect("failed to load");
233+
let calyx_fileid = handler.add_file("calyx_go_done.prot".to_string(), input);
234+
235+
// 2) create transaction
236+
let mut calyx_go_done = Transaction::new("calyx_go_done".to_string());
237+
calyx_go_done.args = vec![Arg::new(ii, Dir::In), Arg::new(oo, Dir::Out)];
238+
calyx_go_done.type_args = vec![dut];
239+
240+
// 3) create expressions
241+
let ii_expr = calyx_go_done.e(Expr::Sym(ii));
242+
calyx_go_done.add_expr_loc(ii_expr, 153, 155, calyx_fileid);
243+
let dut_oo_expr = calyx_go_done.e(Expr::Sym(dut_oo));
244+
calyx_go_done.add_expr_loc(dut_oo_expr, 260, 266, calyx_fileid);
245+
let one_expr = calyx_go_done.e(Expr::Const(BitVecValue::from_u64(1, 1)));
246+
calyx_go_done.add_expr_loc(one_expr, 170, 171, calyx_fileid);
247+
let zero_expr = calyx_go_done.e(Expr::Const(BitVecValue::from_u64(0, 1)));
248+
calyx_go_done.add_expr_loc(zero_expr, 232, 233, calyx_fileid);
249+
let dut_done_expr = calyx_go_done.e(Expr::Sym(dut_done));
250+
calyx_go_done.add_expr_loc(dut_done_expr, 184, 192, calyx_fileid);
251+
let cond_expr = calyx_go_done.e(Expr::Equal(dut_done_expr, one_expr));
252+
calyx_go_done.add_expr_loc(cond_expr, 183, 198, calyx_fileid);
253+
let not_expr = calyx_go_done.e(Expr::Not(cond_expr));
254+
calyx_go_done.add_expr_loc(not_expr, 182, 198, calyx_fileid);
255+
256+
// 4) create statements
257+
let while_body = vec![calyx_go_done.s(Stmt::Step)];
258+
let wbody = calyx_go_done.s(Stmt::Block(while_body));
259+
260+
let dut_ii_assign = calyx_go_done.s(Stmt::Assign(dut_ii, ii_expr));
261+
calyx_go_done.add_stmt_loc(dut_ii_assign, 143, 157, calyx_fileid);
262+
let dut_go_assign = calyx_go_done.s(Stmt::Assign(dut_go, one_expr));
263+
calyx_go_done.add_stmt_loc(dut_go_assign, 160, 172, calyx_fileid);
264+
let dut_while = calyx_go_done.s(Stmt::While(not_expr, wbody));
265+
calyx_go_done.add_stmt_loc(dut_while, 175, 219, calyx_fileid);
266+
let dut_go_reassign = calyx_go_done.s(Stmt::Assign(dut_go, zero_expr));
267+
calyx_go_done.add_stmt_loc(dut_go_reassign, 222, 234, calyx_fileid);
268+
let dut_ii_dontcare = calyx_go_done.s(Stmt::Assign(dut_ii, calyx_go_done.expr_dont_care()));
269+
calyx_go_done.add_stmt_loc(dut_ii_dontcare, 238, 250, calyx_fileid);
270+
let oo_assign = calyx_go_done.s(Stmt::Assign(oo, dut_oo_expr));
271+
calyx_go_done.add_stmt_loc(oo_assign, 254, 268, calyx_fileid);
272+
let body = vec![
273+
dut_ii_assign,
274+
dut_go_assign,
275+
dut_while,
276+
dut_go_reassign,
277+
dut_ii_dontcare,
278+
oo_assign,
279+
];
280+
calyx_go_done.body = calyx_go_done.s(Stmt::Block(body));
281+
type_check(&calyx_go_done, &symbols, &mut handler);
282+
}
283+
}

0 commit comments

Comments
 (0)