Skip to content

Recursion bug due to module.build() connection tracing #709

Description

@desmonddak

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<InvalidHierarchyException>()),
    );
  });
}

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

Additional: Context

The simplest fix would look like:

void _checkPortConnectionsRecursively({
  Set<Module>? visited,
}) {
  final marked = visited ?? <Module>{};

  // 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);
  }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions