Recursion bug due to module.build() connection tracing
- Dominant language
- Dart
- Stars
- 489
- Forks
- 88
- Avg merge
- 3d 23h
- Merged PRs (30d)
- 10
Description
### Describe the bug
With some module miswirings we can get a `StackOverflow` due to infinite recursion.
### To Reproduce
Run the following test:
```
import 'package:rohd/rohd.dart';
import 'package:test/test.dart';
class EmptyModule extends Module {
EmptyModule({super.name});
}
class RecursivePortCheckRepro extends Module {
RecursivePortCheckRepro() : super(name: 'top') {
final driver = Logic();
final topA = addInput('a', driver);
final child = EmptyModule(name: 'child');
final childA = child.addInput('a', topA);
// The `and()` avoids Logic's immediate direct self-connection rejection
// while closing the signal graph.
driver <= childA.and();
}
}
void main() {
test('discovers a cyclic hierarchy', () async {
await expectLater(
RecursivePortCheckRepro().build(),
throwsA(isA()),
);
});
}
```
### Expected behavior
An error reported on the bad connection in the parent to a child `Logic`.
### Actual behavior
`StackOverflow`
### Additional: Dart SDK info
_No response_
### Additional: pubspec.yaml
```yaml
```
### Additional: Context
The simplest fix would look like:
```
void _checkPortConnectionsRecursively({
Set? visited,
}) {
final marked = visited ?? {};
// Mark before descending. This breaks cycles such as top -> child -> top.
if (!marked.add(this)) {
return;
}
_checkPortConnections();
for (final subModule in _subModules) {
subModule._checkPortConnectionsRecursively(visited: marked);
}
}
```
Contributor guide
Research direction
Start at Module.build() and the _checkPortConnectionsRecursively entry point, then reproduce the cycle with the test case shown in the issue. Add a regression test for the cyclic hierarchy and verify that build() reports InvalidHierarchyException for the bad parent-to-child connection instead of StackOverflow.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100