Skip to content

fix(compiler): nested for loops incorrectly calculating computed variables#52931

Closed
crisbeto wants to merge 1 commit intoangular:mainfrom
crisbeto:52917/compiler-shadowed-computed
Closed

fix(compiler): nested for loops incorrectly calculating computed variables#52931
crisbeto wants to merge 1 commit intoangular:mainfrom
crisbeto:52917/compiler-shadowed-computed

Conversation

@crisbeto
Copy link
Copy Markdown
Member

The $first, $last, $even and $odd variables in @for loops aren't defined on the template context of the loop, but are computed based on $index and $count (e.g. $first is defined as $index === 0). We do this calculation by looking up $index and $count when one of the variables is used.

The problem is that all @for loop variables are available implicitly which means that when a nested loop tries to rewrite a reference to an outer loop computed variable, it finds its own $index and $count first and it doesn't look up the ones on the parent at all. This means that the calculated values will be incorrect at runtime.

These changes work around the issue by defining nested-level-specific variable names that can be used for lookups (e.g. $index at level 2 will also be available as ɵ$index_2). This isn't the most elegant solution, however the TemplatDefitinionBuilder wasn't set up to handle shadowed variables like this and it doesn't make sense to refactor it given the upcoming template pipeline.

Fixes #52917.

@crisbeto crisbeto added action: review The PR is still awaiting reviews from at least one requested reviewer target: patch This PR is targeted for the next patch release area: compiler Issues related to `ngc`, Angular's template compiler labels Nov 15, 2023
@ngbot ngbot bot modified the milestone: Backlog Nov 15, 2023
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: my initial thinking here was that we could just skip over the current scope and look up $index in the parent scope. This doesn't work, because the variable expression is cached which will cause all other usages of $index in the template to refer to the implicit $index of the parent template, rather than the local one.

@crisbeto crisbeto force-pushed the 52917/compiler-shadowed-computed branch from b9a8711 to b20040e Compare November 15, 2023 13:09
@crisbeto crisbeto requested a review from dylhunn November 15, 2023 14:05
@crisbeto crisbeto marked this pull request as ready for review November 15, 2023 14:05
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dylhunn I didn't do this for the template pipeline, because you mentioned that you're moving some code around there. Also I think this is severe enough that we should get a fix out for users as quickly as we can.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably should pursue a different solution in the template pipeline. Perhaps making everything a proper named variable, instead of inlining aliases ($first -> $index === 0) at the site of usage.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that it would be optimal to just have these on the context, but I think we did it this way for perf reasons (cc @pkozlowski-opensource).

…ables

The `$first`, `$last`, `$even` and `$odd` variables in `@for` loops aren't defined on the template context of the loop, but are computed based on `$index` and `$count` (e.g. `$first` is defined as `$index === 0`). We do this calculation by looking up `$index` and `$count` when one of the variables is used.

The problem is that all `@for` loop variables are available implicitly which means that when a nested loop tries to rewrite a reference to an outer loop computed variable, it finds its own `$index` and `$count` first and it doesn't look up the ones on the parent at all. This means that the calculated values will be incorrect at runtime.

These changes work around the issue by defining nested-level-specific variable names that can be used for lookups (e.g. `$index` at level `2` will also be available as `ɵ$index_2`). This isn't the most elegant solution, however the `TemplatDefitinionBuilder` wasn't set up to handle shadowed variables like this and it doesn't make sense to refactor it given the upcoming template pipeline.

Fixes angular#52917.
@crisbeto crisbeto force-pushed the 52917/compiler-shadowed-computed branch from b20040e to 38e727a Compare November 16, 2023 05:46
@crisbeto
Copy link
Copy Markdown
Member Author

Pushed a change to align the test output with #52935.

Copy link
Copy Markdown
Contributor

@dylhunn dylhunn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense as a temporary workaround.

@crisbeto crisbeto added action: merge The PR is ready for merge by the caretaker and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Nov 16, 2023
@AndrewKushnir
Copy link
Copy Markdown
Contributor

This PR was merged into the repository by commit d9d566d.

AndrewKushnir pushed a commit that referenced this pull request Nov 16, 2023
…ables (#52931)

The `$first`, `$last`, `$even` and `$odd` variables in `@for` loops aren't defined on the template context of the loop, but are computed based on `$index` and `$count` (e.g. `$first` is defined as `$index === 0`). We do this calculation by looking up `$index` and `$count` when one of the variables is used.

The problem is that all `@for` loop variables are available implicitly which means that when a nested loop tries to rewrite a reference to an outer loop computed variable, it finds its own `$index` and `$count` first and it doesn't look up the ones on the parent at all. This means that the calculated values will be incorrect at runtime.

These changes work around the issue by defining nested-level-specific variable names that can be used for lookups (e.g. `$index` at level `2` will also be available as `ɵ$index_2`). This isn't the most elegant solution, however the `TemplatDefitinionBuilder` wasn't set up to handle shadowed variables like this and it doesn't make sense to refactor it given the upcoming template pipeline.

Fixes #52917.

PR Close #52931
@JeanMeche JeanMeche mentioned this pull request Nov 18, 2023
@angular-automatic-lock-bot
Copy link
Copy Markdown

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Dec 17, 2023
ChellappanRajan pushed a commit to ChellappanRajan/angular that referenced this pull request Jan 23, 2024
…ables (angular#52931)

The `$first`, `$last`, `$even` and `$odd` variables in `@for` loops aren't defined on the template context of the loop, but are computed based on `$index` and `$count` (e.g. `$first` is defined as `$index === 0`). We do this calculation by looking up `$index` and `$count` when one of the variables is used.

The problem is that all `@for` loop variables are available implicitly which means that when a nested loop tries to rewrite a reference to an outer loop computed variable, it finds its own `$index` and `$count` first and it doesn't look up the ones on the parent at all. This means that the calculated values will be incorrect at runtime.

These changes work around the issue by defining nested-level-specific variable names that can be used for lookups (e.g. `$index` at level `2` will also be available as `ɵ$index_2`). This isn't the most elegant solution, however the `TemplatDefitinionBuilder` wasn't set up to handle shadowed variables like this and it doesn't make sense to refactor it given the upcoming template pipeline.

Fixes angular#52917.

PR Close angular#52931
rlmestre pushed a commit to rlmestre/angular that referenced this pull request Jan 26, 2024
…ables (angular#52931)

The `$first`, `$last`, `$even` and `$odd` variables in `@for` loops aren't defined on the template context of the loop, but are computed based on `$index` and `$count` (e.g. `$first` is defined as `$index === 0`). We do this calculation by looking up `$index` and `$count` when one of the variables is used.

The problem is that all `@for` loop variables are available implicitly which means that when a nested loop tries to rewrite a reference to an outer loop computed variable, it finds its own `$index` and `$count` first and it doesn't look up the ones on the parent at all. This means that the calculated values will be incorrect at runtime.

These changes work around the issue by defining nested-level-specific variable names that can be used for lookups (e.g. `$index` at level `2` will also be available as `ɵ$index_2`). This isn't the most elegant solution, however the `TemplatDefitinionBuilder` wasn't set up to handle shadowed variables like this and it doesn't make sense to refactor it given the upcoming template pipeline.

Fixes angular#52917.

PR Close angular#52931
amilamen pushed a commit to amilamen/angular that referenced this pull request Jan 26, 2024
…ables (angular#52931)

The `$first`, `$last`, `$even` and `$odd` variables in `@for` loops aren't defined on the template context of the loop, but are computed based on `$index` and `$count` (e.g. `$first` is defined as `$index === 0`). We do this calculation by looking up `$index` and `$count` when one of the variables is used.

The problem is that all `@for` loop variables are available implicitly which means that when a nested loop tries to rewrite a reference to an outer loop computed variable, it finds its own `$index` and `$count` first and it doesn't look up the ones on the parent at all. This means that the calculated values will be incorrect at runtime.

These changes work around the issue by defining nested-level-specific variable names that can be used for lookups (e.g. `$index` at level `2` will also be available as `ɵ$index_2`). This isn't the most elegant solution, however the `TemplatDefitinionBuilder` wasn't set up to handle shadowed variables like this and it doesn't make sense to refactor it given the upcoming template pipeline.

Fixes angular#52917.

PR Close angular#52931
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

action: merge The PR is ready for merge by the caretaker area: compiler Issues related to `ngc`, Angular's template compiler target: patch This PR is targeted for the next patch release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nested for loops reset parent even/odd

3 participants