Skip to content

Fix code generation for a list of lists inside a conditional or switch - #1340

Open
SimonCockx wants to merge 4 commits into
mainfrom
fix-codegen-bug
Open

Fix code generation for a list of lists inside a conditional or switch#1340
SimonCockx wants to merge 4 commits into
mainfrom
fix-codegen-bug

Conversation

@SimonCockx

Copy link
Copy Markdown
Contributor

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: a list of lists is now also rejected as an operand of a binary operation (e.g. default) and as an argument of a function or rule call. Both cases 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.

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: six 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.
@netlify

netlify Bot commented Jul 27, 2026

Copy link
Copy Markdown

Deploy Preview for finos-rune canceled.

Name Link
🔨 Latest commit 7ac8468
🔍 Latest deploy log https://app.netlify.com/projects/finos-rune/deploys/6a7259939f6ebb0008efeeb9

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.
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