use super::*;
impl Package {
pub(crate) fn check(&self) -> Result<(), Vec<BitsyError>> {
let mut errors: Vec<BitsyError> = vec![];
for item in self.items() {
match item {
Item::EnumTypeDef(typedef) => {
if let Err(errs) = self.check_enum_typedef(typedef.clone()) {
for error in errs {
errors.push(error)
}
}
},
_ => (),
}
}
for fndef in self.fndefs() {
if let Err(fndef_errors) = self.check_typecheck_fndef(fndef.clone()) {
for fndef_error in fndef_errors {
errors.push(fndef_error);
}
}
}
for moddef in self.moddefs() {
if let Err(component_errors) = self.check_component(moddef.clone()) {
for component_error in component_errors {
errors.push(component_error);
}
}
for submod in moddef.submods() {
if let Err(component_errors) = self.check_component(submod) {
for component_error in component_errors {
errors.push(component_error);
}
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
fn check_enum_typedef(&self, typedef: Arc<EnumTypeDef>) -> Result<(), Vec<BitsyError>> {
let mut names = BTreeSet::new();
let mut values = BTreeSet::new();
let mut errors = vec![];
for (name, value) in &typedef.values {
if !names.insert(name.clone()) {
errors.push(BitsyError::Unknown(Some(typedef.span.clone()), format!("Duplicate name in enum: {name}")));
}
if !values.insert(value.value()) {
errors.push(BitsyError::Unknown(Some(typedef.span.clone()), format!("Duplicate value in enum: {value:?}")));
}
}
if errors.len() > 0 {
Err(errors)
} else {
Ok(())
}
}
fn check_component(&self, component: Arc<Component>) -> Result<(), Vec<BitsyError>> {
let mut errors = vec![];
match &*component {
Component::Mod(_loc, _name, _children, _wires, _whens) => {
errors.extend(self.check_typecheck_component(component.clone()));
errors.extend(self.check_wires_no_such_component(component.clone()));
errors.extend(self.check_children_duplicate_names(component.clone()));
errors.extend(self.check_wires_duplicate_targets(component.clone()));
errors.extend(self.check_missing_drivers(component.clone()));
errors.extend(self.check_wires_wiretype(component.clone()));
errors.extend(self.check_incoming_port_driven(component.clone()));
},
Component::Ext(loc, _name, children) => {
for component in children {
if !component.is_port() {
errors.push(BitsyError::ExtHasNonPort(loc.clone(), component.name().to_string()));
}
}
},
_ => (),
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
fn check_children_duplicate_names(&self, component: Arc<Component>) -> Vec<BitsyError> {
let mut errors = vec![];
let mut seen = BTreeSet::new();
for child in &component.children() {
if !seen.contains(child.name()) {
seen.insert(child.name());
} else {
errors.push(BitsyError::DuplicateComponent(child.clone()));
}
}
errors
}
fn check_wires_duplicate_targets(&self, component: Arc<Component>) -> Vec<BitsyError> {
let mut errors = vec![];
let mut seen = BTreeSet::new();
for Wire(loc, target, _expr, _typ) in &component.wires() {
if !seen.contains(target) {
seen.insert(target);
} else {
errors.push(BitsyError::MultipleDrivers(loc.clone(), target.to_string()));
}
}
errors
}
fn check_typecheck_fndef(&self, fndef: Arc<FnDef>) -> Result<(), Vec<BitsyError>> {
let mut errors = vec![];
match fndef.body.typecheck(fndef.ret.clone(), fndef.context()) {
Err(e) => errors.push(BitsyError::TypeError(e)),
Ok(()) => fndef.body.assert_has_types(),
}
if errors.len() > 0 {
Err(errors)
} else {
Ok(())
}
}
fn check_typecheck_component(&self, component: Arc<Component>) -> Vec<BitsyError> {
let ctx = self.context_for(component.clone());
let mut errors = vec![];
for Wire(loc, target, expr, _wiretype) in &component.wires() {
let target_typ = if let Some(typ) = ctx.lookup(target) {
typ
} else {
errors.push(BitsyError::NoSuchComponent(loc.clone(), target.to_string()));
continue;
};
match expr.typecheck(target_typ, ctx.clone()) {
Err(e) => errors.push(BitsyError::TypeError(e)),
Ok(()) => expr.assert_has_types(),
}
}
for When(expr, wires) in &component.whens() {
match expr.typecheck(Type::Word(1), ctx.clone()) {
Err(e) => errors.push(BitsyError::TypeError(e)),
Ok(()) => expr.assert_has_types(),
}
for Wire(loc, target, expr, _wiretype) in wires {
let target_typ = if let Some(typ) = ctx.lookup(target) {
typ
} else {
errors.push(BitsyError::NoSuchComponent(loc.clone(), target.to_string()));
continue;
};
match expr.typecheck(target_typ, ctx.clone()) {
Err(e) => errors.push(BitsyError::TypeError(e)),
Ok(()) => expr.assert_has_types(),
}
}
}
for child in component.children() {
if let Component::Reg(_loc, _name, _typ, Some(reset)) = &*child {
let typ = self.type_of(child.clone()).unwrap();
match reset.typecheck(typ, ctx.clone()) {
Err(e) => errors.push(BitsyError::TypeError(e)),
Ok(()) => reset.assert_has_types(),
}
}
}
errors
}
fn check_wires_no_such_component(&self, component: Arc<Component>) -> Vec<BitsyError> {
let mut errors = vec![];
for Wire(loc, target, _expr, _wiretype) in &component.wires() {
if self.component_from(component.clone(), target.clone()).is_none() {
errors.push(BitsyError::NoSuchComponent(loc.clone(), target.to_string()));
}
}
errors
}
fn check_wires_wiretype(&self, component: Arc<Component>) -> Vec<BitsyError> {
let mut errors = vec![];
for Wire(loc, target, _expr, wiretype) in &component.wires() {
if let Some(component) = self.component_from(component.clone(), target.clone()) {
match (&*component, wiretype) {
(Component::Reg(_loc, name, _typ, _reset), WireType::Direct) => {
errors.push(BitsyError::WrongWireType(loc.clone(), name.clone(), WireType::Direct))
},
(Component::Node(_loc, name, _typ), WireType::Latch) => {
errors.push(BitsyError::WrongWireType(loc.clone(), name.clone(), WireType::Latch))
},
(Component::Outgoing(_loc, name, _typ), WireType::Latch) => {
errors.push(BitsyError::WrongWireType(loc.clone(), name.clone(), WireType::Latch))
},
(_, _) => (),
}
}
}
errors
}
fn check_incoming_port_driven(&self, component: Arc<Component>) -> Vec<BitsyError> {
let mut errors = vec![];
for Wire(_loc, target, _expr, _wiretype) in &component.wires() {
if let Some(component) = self.component_from(component.clone(), target.clone()) {
let is_local = !target.contains(".");
if is_local {
match &*component {
Component::Incoming(loc, name, _typ) => {
errors.push(BitsyError::IncomingPortDriven(loc.clone(), name.clone()))
},
_ => (),
}
}
}
}
errors
}
fn check_missing_drivers(&self, component: Arc<Component>) -> Vec<BitsyError> {
let mut errors = vec![];
let mut terminals_remaining: Vec<(Path, Arc<Component>)> = self.visible_paths(component.clone());
for Wire(_loc, target, _expr, _typ) in &component.wires() {
terminals_remaining = terminals_remaining.into_iter().filter(|(path, _component)| path != target).collect();
}
for When(_expr, wires) in &component.whens() {
for Wire(_loc, target, _expr, _typ) in wires {
terminals_remaining = terminals_remaining.into_iter().filter(|(path, _component)| path != target).collect();
}
}
for (path, remaining_component) in terminals_remaining.into_iter() {
let mut is_incoming_port = false;
if let Component::Incoming(_loc, _name, _typ) = &*remaining_component {
is_incoming_port = true;
}
let is_local = !path.contains(".");
if !is_local && is_incoming_port {
let inst_component = self.component_from(component.clone(), path.parent()).unwrap();
errors.push(BitsyError::NoDriversPort(inst_component.clone(), remaining_component.clone()));
} else if is_local && !is_incoming_port {
errors.push(BitsyError::NoDrivers(remaining_component.clone()));
}
}
errors
}
}