Skip to content

Commit 88132bf

Browse files
committed
Add lint for correct usage of split
1 parent fc05b75 commit 88132bf

7 files changed

Lines changed: 155 additions & 6 deletions

File tree

src/flattening/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,16 @@ impl DeclarationKind {
620620
DeclarationKind::RegularGenerative | DeclarationKind::TemplateParameter(..) => true,
621621
}
622622
}
623+
pub fn num_splits(&self) -> usize {
624+
match self {
625+
DeclarationKind::RegularWire { num_splits, .. } => *num_splits,
626+
DeclarationKind::StructField(_)
627+
| DeclarationKind::Port { .. }
628+
| DeclarationKind::ConditionalBinding { .. }
629+
| DeclarationKind::RegularGenerative
630+
| DeclarationKind::TemplateParameter(_) => 0,
631+
}
632+
}
623633
pub fn is_state(&self) -> bool {
624634
match self {
625635
DeclarationKind::RegularWire { is_state, .. }

src/flattening/typecheck/lints.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub fn perform_lints(pass: &mut LinkerPass, errors: &ErrorCollector, linker_file
2929
ctx.no_duplicate_ports();
3030
ctx.check_unsynthesizeable_types();
3131
ctx.no_calling_local_actions();
32+
ctx.splits_are_used_correctly();
3233
}
3334

3435
struct LintContext<'l> {
@@ -607,4 +608,78 @@ impl LintContext<'_> {
607608
}
608609
}
609610
}
611+
612+
// ==== splits ====
613+
fn check_splits_for_wire_ref(&self, wr: &WireReference) {
614+
let mut path_iter = wr.path.iter();
615+
let decl = match &wr.root {
616+
WireReferenceRoot::LocalDecl(decl_id) => {
617+
self.working_on.instructions[*decl_id].unwrap_declaration()
618+
}
619+
WireReferenceRoot::LocalSubmodule(_)
620+
| WireReferenceRoot::LocalInterface(_)
621+
| WireReferenceRoot::NamedConstant(_)
622+
| WireReferenceRoot::NamedModule(_)
623+
| WireReferenceRoot::Error => return,
624+
};
625+
626+
let decl_name = &decl.name;
627+
let num_splits = decl.decl_kind.num_splits();
628+
for _ in 0..num_splits {
629+
match path_iter.next() {
630+
Some(WireReferencePathElement::FieldAccess { name_span, .. }) => {
631+
self.errors
632+
.error(*name_span, format!("All split dimensions must be indexed. {decl_name} has {num_splits} splits."))
633+
.info_obj(decl);
634+
}
635+
None => {
636+
self.errors
637+
.error(wr.root_span, format!("All split dimensions must be indexed. {decl_name} has {num_splits} splits."))
638+
.info_obj(decl);
639+
}
640+
Some(WireReferencePathElement::ArraySlice { bracket_span, .. })
641+
| Some(WireReferencePathElement::ArrayPartSelect { bracket_span, .. }) => {
642+
self.errors
643+
.error(bracket_span.inner_span(), format!("Split dimensions cannot be sliced. {decl_name} has {num_splits} splits."))
644+
.info_obj(decl);
645+
}
646+
Some(WireReferencePathElement::ArrayAccess { idx, bracket_span }) => {
647+
let idx = self.working_on.instructions[*idx].unwrap_subexpression();
648+
if !idx.domain.is_generative() {
649+
self.errors
650+
.error(bracket_span.inner_span(), format!("Indexing a split dimension must be generative. {decl_name} has {num_splits} splits."))
651+
.info_obj(decl);
652+
}
653+
}
654+
}
655+
}
656+
}
657+
fn splits_are_used_correctly(&self) {
658+
for (_, instr) in &self.working_on.instructions {
659+
match instr {
660+
Instruction::Declaration(decl) => {
661+
if let Some(num_arrays) = decl.typ.rank.count() {
662+
let num_splits = decl.decl_kind.num_splits();
663+
if num_arrays < num_splits {
664+
self.errors.error(decl.decl_span, format!("`split` requires the declaration's type to have as many array levels as `split` keywords provided. Found {num_arrays} arrays, and {num_splits} splits."));
665+
}
666+
}
667+
}
668+
Instruction::Expression(expression) => {
669+
if let ExpressionSource::WireRef(wr) = &expression.source {
670+
self.check_splits_for_wire_ref(wr);
671+
}
672+
if let ExpressionOutput::MultiWrite(mr) = &expression.output {
673+
for w in mr {
674+
self.check_splits_for_wire_ref(&w.to);
675+
}
676+
}
677+
}
678+
Instruction::SubModule(_)
679+
| Instruction::Interface(_)
680+
| Instruction::IfStatement(_)
681+
| Instruction::ForStatement(_) => {}
682+
}
683+
}
684+
}
610685
}

src/instantiation/execute.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,7 +1151,7 @@ impl<'l> ExecutionContext<'l> {
11511151
}
11521152

11531153
fn alloc_array_dimensions_stack(&mut self, peano_type: &PeanoType) -> Vec<UniCell<Value>> {
1154-
vec![Value::UNKNOWN; peano_type.count()]
1154+
vec![Value::UNKNOWN; peano_type.count_unwrap()]
11551155
}
11561156
fn expression_to_real_wire(
11571157
&mut self,
@@ -1367,7 +1367,7 @@ impl<'l> ExecutionContext<'l> {
13671367
}
13681368
ExpressionSource::UnaryOp { op, rank, right } => {
13691369
let right_val = self.generation_state.get_generation_value(*right)?;
1370-
duplicate_for_all_array_ranks(&[right_val], rank.count(), &mut |[v]| {
1370+
duplicate_for_all_array_ranks(&[right_val], rank.count_unwrap(), &mut |[v]| {
13711371
Ok(compute_unary_op(*op, v))
13721372
})
13731373
.unwrap()
@@ -1383,7 +1383,7 @@ impl<'l> ExecutionContext<'l> {
13831383

13841384
duplicate_for_all_array_ranks(
13851385
&[left_val, right_val],
1386-
rank.count(),
1386+
rank.count_unwrap(),
13871387
&mut |[l, r]| compute_binary_op(l, *op, r),
13881388
)
13891389
.map_err(|reason| (expr.span, reason))?

src/typing/abstract_type.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ impl PeanoType {
152152
#[allow(clippy::declare_interior_mutable_const)]
153153
pub const UNKNOWN: UniCell<PeanoType> = UniCell::UNKNOWN;
154154

155-
pub fn count(&self) -> usize {
155+
pub fn count_unwrap(&self) -> usize {
156156
let mut cur = self;
157157
let mut sum = 0;
158158

@@ -174,6 +174,20 @@ impl PeanoType {
174174
}
175175
}
176176

177+
impl UniCell<PeanoType> {
178+
pub fn count(&self) -> Option<usize> {
179+
let mut cur = self.get()?;
180+
let mut sum = 0;
181+
182+
while let PeanoType::Succ(succ) = cur {
183+
cur = succ.get()?;
184+
sum += 1;
185+
}
186+
187+
Some(sum)
188+
}
189+
}
190+
177191
#[derive(Debug, Clone, PartialEq, Eq)]
178192
pub struct AbstractGlobalReference<ID> {
179193
pub id: ID,
@@ -190,7 +204,7 @@ impl AbstractRankedType {
190204
AbstractInnerType::Template(id) => args[*id]
191205
.unwrap_type()
192206
.clone()
193-
.rank_up_multi(self.rank.count()),
207+
.rank_up_multi(self.rank.count_unwrap()),
194208
AbstractInnerType::Named(named_ref) => {
195209
AbstractInnerType::Named(named_ref.substitute_template_args(args))
196210
.with_rank(self.rank.unwrap().clone())

src/typing/value_unifier.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ impl Value {
206206
abs_typ: &AbstractRankedType,
207207
template_args: &TVec<ConcreteTemplateArg>,
208208
) -> Result<ConcreteType, String> {
209-
let array_depth = abs_typ.rank.count();
209+
let array_depth = abs_typ.rank.count_unwrap();
210210
let mut tensor_sizes = Vec::with_capacity(array_depth);
211211

212212
let content_typ = match abs_typ.inner.unwrap() {

test.sus

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1763,3 +1763,13 @@ module TestNextPow2 {
17631763
// Should error
17641764
nextPow2#(V: -1)
17651765
}
1766+
1767+
module TestSplits {
1768+
input bool y
1769+
split bool[5] x
1770+
1771+
x[0] = y
1772+
for int i in 1..5 {
1773+
reg x[i] = x[i-1]
1774+
}
1775+
}

test.sus_errors.txt

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)