Skip to content

[9.x.x] - Fix code generation for a list of lists inside a conditional or switch (#1340) - #1353

Open
SimonCockx wants to merge 2 commits into
9.x.xfrom
backport/list-of-lists-9xx
Open

[9.x.x] - Fix code generation for a list of lists inside a conditional or switch (#1340)#1353
SimonCockx wants to merge 2 commits into
9.x.xfrom
backport/list-of-lists-9xx

Conversation

@SimonCockx

Copy link
Copy Markdown
Contributor

Backport of #1340 to 9.x.x.

A list of lists that appears in a branch of a conditional or switch expression is not detected as such, which results in generated Java code that does not compile. Reported on a CDM extension model, where

add resolvedResetDates -> dates:
    if frequencyMatches
    then baseResetDates
    else
    if isWeeklyRoll
    then calculationPeriodsData extract GenerateWeeklyResetSchedule(...)

generated .addDates(ifThenElseResult) with ifThenElseResult typed as java.lang.Object:

error: no suitable method found for addDates(java.lang.Object)

Cause

CardinalityProvider#isOutputListOfLists only walked through extract, filter, then and flatten, never into the branches of a conditional or switch. Since GenerateWeeklyResetSchedule outputs a list and calculationPeriodsData is a list, that extract produces a list of lists, but it stayed invisible to checkFunctionOperation, so the model validated without issues.

Code generation then emitted a MapperListOfLists for that branch and a List for the other one. TypeCoercionService has no conversion out of MapperListOfLists and silently leaves the expression unchanged when it cannot convert, so JavaIfThenElseBuilder joined the two branch types into java.lang.Object.

Minimal reproduction:

type Foo:
    xs string (0..*)

func FuncFoo:
    inputs:
        foos Foo (0..*)
        test boolean (1..1)
    output:
        result string (0..*)

    add result:
        if test
        then foos extract item -> xs

Changes

  • CardinalityProvider: a conditional or switch is a list of lists if any of its branches is. Models like the one above are now reported with the existing Assign expression contains a list of lists, use flatten to create a list error.
  • ExpressionValidator: all branches of a conditional or switch must agree on whether they are a list of lists (empty branches excepted) - there is no representation for a mix of the two.
  • ExpressionValidator and ConstructorValidator: a list of lists is now also rejected as an operand of a binary operation (e.g. default), as an argument of a function or rule call, as an element of a list literal and as the value of a constructor attribute. All of these used to generate Java code that does not compile as well, without any validation error.
  • ExpressionGenerator and TypeCoercionService: then and the empty representation of a MapperListOfLists are supported, so a conditional or switch that consistently produces a list of lists can be flattened afterwards, e.g. (if test then foos extract item -> xs) flatten.

The workaround for existing models is to flatten inside the branch: then (calculationPeriodsData extract GenerateWeeklyResetSchedule(...) then flatten).

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Note that models which relied on the unreported cases above will now get a validation error, but they could not have compiled before. Validating the common-domain-model and digital-regulatory-reporting Rune sources with and without this change produces an identical set of issues.

Tests

  • ListOfListsTest: new, covers generation and evaluation of a list of lists inside a conditional and a switch, followed by flatten or then flatten.
  • ExpressionValidatorTest: eight new cases for the validations above.

`CardinalityProvider#isOutputListOfLists` did not look inside the branches
of a conditional or switch expression. As a result, a branch such as

    if test
    then foos extract item -> xs   // a list of lists

passed validation, after which the generator emitted a `MapperListOfLists`
for that branch and a `List` for the other one. Since `TypeCoercionService`
has no conversion out of `MapperListOfLists`, it silently left the branch
as is and both branches were joined into `java.lang.Object`, producing Java
code that does not compile.

A conditional or switch is now considered a list of lists if any of its
branches is, which means such models are reported with the existing
"use flatten to create a list" error instead. On top of that:

- All branches of a conditional or switch must agree on whether they are a
  list of lists (empty branches excepted), since there is no representation
  for a mix of the two.
- A list of lists is now also rejected as an operand of a binary operation
  and as an argument of a function or rule call. Both cases used to generate
  Java code that does not compile as well.
- `then` and the empty representation of `TypeCoercionService` now support
  `MapperListOfLists`, so that a conditional or switch that consistently
  produces a list of lists can be flattened afterwards.
A list of lists as an element of a list literal or as the value of a
constructor attribute generated Java code that does not compile as well,
without any validation error. Extract the check into a shared
`isNotListOfListsCheck` and apply it consistently to operands of a binary
operation, arguments of a function or rule call, elements of a list literal
and values of a constructor.
@SimonCockx
SimonCockx force-pushed the backport/list-of-lists-9xx branch from e6fe92c to 4b45739 Compare August 3, 2026 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant