-
Notifications
You must be signed in to change notification settings - Fork 49.9k
[compiler] Fix for destructuring with mixed declaration/reassignment #35144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,11 @@ import { | |
| promoteTemporary, | ||
| } from '../HIR'; | ||
| import {clonePlaceToTemporary} from '../HIR/HIRBuilder'; | ||
| import {eachPatternOperand, mapPatternOperands} from '../HIR/visitors'; | ||
| import { | ||
| eachInstructionLValueWithKind, | ||
| eachPatternOperand, | ||
| mapPatternOperands, | ||
| } from '../HIR/visitors'; | ||
| import { | ||
| ReactiveFunctionTransform, | ||
| Transformed, | ||
|
|
@@ -113,14 +117,18 @@ class Visitor extends ReactiveFunctionTransform<State> { | |
| ): Transformed<ReactiveStatement> { | ||
| this.visitInstruction(instruction, state); | ||
|
|
||
| let instructionsToProcess: Array<ReactiveInstruction> = [instruction]; | ||
| let result: Transformed<ReactiveStatement> = {kind: 'keep'}; | ||
|
|
||
| if (instruction.value.kind === 'Destructure') { | ||
| const transformed = transformDestructuring( | ||
| state, | ||
| instruction, | ||
| instruction.value, | ||
| ); | ||
| if (transformed) { | ||
| return { | ||
| instructionsToProcess = transformed; | ||
| result = { | ||
| kind: 'replace-many', | ||
| value: transformed.map(instruction => ({ | ||
| kind: 'instruction', | ||
|
|
@@ -129,7 +137,17 @@ class Visitor extends ReactiveFunctionTransform<State> { | |
| }; | ||
| } | ||
| } | ||
| return {kind: 'keep'}; | ||
|
|
||
| // Update state.declared with declarations from the instruction(s) | ||
| for (const instr of instructionsToProcess) { | ||
| for (const [place, kind] of eachInstructionLValueWithKind(instr)) { | ||
| if (kind !== InstructionKind.Reassign) { | ||
| state.declared.add(place.identifier.declarationId); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -144,10 +162,13 @@ function transformDestructuring( | |
| const isDeclared = state.declared.has(place.identifier.declarationId); | ||
| if (isDeclared) { | ||
| reassigned.add(place.identifier.id); | ||
| } else { | ||
| hasDeclaration = true; | ||
| } | ||
| hasDeclaration ||= !isDeclared; | ||
| } | ||
| if (reassigned.size === 0 || !hasDeclaration) { | ||
| if (!hasDeclaration) { | ||
| // all reassignments | ||
| destructure.lvalue.kind = InstructionKind.Reassign; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm just curious -- was this line necessary? We previously didn't need to always either overwrite or construct a new
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, because before these cases would have fallen through to the code below which replaced the instruction with a Reassign kind. |
||
| return null; | ||
| } | ||
| /* | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
|
|
||
| ## Input | ||
|
|
||
| ```javascript | ||
| // @flow @compilationMode:"infer" | ||
| 'use strict'; | ||
|
|
||
| function getWeekendDays(user) { | ||
| return [0, 6]; | ||
| } | ||
|
|
||
| function getConfig(weekendDays) { | ||
| return [1, 5]; | ||
| } | ||
|
|
||
| component Calendar(user, defaultFirstDay, currentDate, view) { | ||
| const weekendDays = getWeekendDays(user); | ||
| let firstDay = defaultFirstDay; | ||
| let daysToDisplay = 7; | ||
| if (view === 'week') { | ||
| let lastDay; | ||
| // this assignment produces invalid code | ||
| [firstDay, lastDay] = getConfig(weekendDays); | ||
| daysToDisplay = ((7 + lastDay - firstDay) % 7) + 1; | ||
| } else if (view === 'day') { | ||
| firstDay = currentDate.getDayOfWeek(); | ||
| daysToDisplay = 1; | ||
| } | ||
|
|
||
| return [currentDate, firstDay, daysToDisplay]; | ||
| } | ||
|
|
||
| export const FIXTURE_ENTRYPOINT = { | ||
| fn: Calendar, | ||
| params: [ | ||
| { | ||
| user: {}, | ||
| defaultFirstDay: 1, | ||
| currentDate: {getDayOfWeek: () => 3}, | ||
| view: 'week', | ||
| }, | ||
| ], | ||
| sequentialRenders: [ | ||
| { | ||
| user: {}, | ||
| defaultFirstDay: 1, | ||
| currentDate: {getDayOfWeek: () => 3}, | ||
| view: 'week', | ||
| }, | ||
| { | ||
| user: {}, | ||
| defaultFirstDay: 1, | ||
| currentDate: {getDayOfWeek: () => 3}, | ||
| view: 'day', | ||
| }, | ||
| ], | ||
| }; | ||
|
|
||
| ``` | ||
|
|
||
| ## Code | ||
|
|
||
| ```javascript | ||
| "use strict"; | ||
| import { c as _c } from "react/compiler-runtime"; | ||
|
|
||
| function getWeekendDays(user) { | ||
| return [0, 6]; | ||
| } | ||
|
|
||
| function getConfig(weekendDays) { | ||
| return [1, 5]; | ||
| } | ||
|
|
||
| function Calendar(t0) { | ||
| const $ = _c(12); | ||
| const { user, defaultFirstDay, currentDate, view } = t0; | ||
| let daysToDisplay; | ||
| let firstDay; | ||
| if ( | ||
| $[0] !== currentDate || | ||
| $[1] !== defaultFirstDay || | ||
| $[2] !== user || | ||
| $[3] !== view | ||
| ) { | ||
| const weekendDays = getWeekendDays(user); | ||
| firstDay = defaultFirstDay; | ||
| daysToDisplay = 7; | ||
| if (view === "week") { | ||
| let lastDay; | ||
|
|
||
| [firstDay, lastDay] = getConfig(weekendDays); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. awesome! |
||
| daysToDisplay = ((7 + lastDay - firstDay) % 7) + 1; | ||
| } else { | ||
| if (view === "day") { | ||
| let t1; | ||
| if ($[6] !== currentDate) { | ||
| t1 = currentDate.getDayOfWeek(); | ||
| $[6] = currentDate; | ||
| $[7] = t1; | ||
| } else { | ||
| t1 = $[7]; | ||
| } | ||
| firstDay = t1; | ||
| daysToDisplay = 1; | ||
| } | ||
| } | ||
| $[0] = currentDate; | ||
| $[1] = defaultFirstDay; | ||
| $[2] = user; | ||
| $[3] = view; | ||
| $[4] = daysToDisplay; | ||
| $[5] = firstDay; | ||
| } else { | ||
| daysToDisplay = $[4]; | ||
| firstDay = $[5]; | ||
| } | ||
| let t1; | ||
| if ($[8] !== currentDate || $[9] !== daysToDisplay || $[10] !== firstDay) { | ||
| t1 = [currentDate, firstDay, daysToDisplay]; | ||
| $[8] = currentDate; | ||
| $[9] = daysToDisplay; | ||
| $[10] = firstDay; | ||
| $[11] = t1; | ||
| } else { | ||
| t1 = $[11]; | ||
| } | ||
| return t1; | ||
| } | ||
|
|
||
| export const FIXTURE_ENTRYPOINT = { | ||
| fn: Calendar, | ||
| params: [ | ||
| { | ||
| user: {}, | ||
| defaultFirstDay: 1, | ||
| currentDate: { getDayOfWeek: () => 3 }, | ||
| view: "week", | ||
| }, | ||
| ], | ||
|
|
||
| sequentialRenders: [ | ||
| { | ||
| user: {}, | ||
| defaultFirstDay: 1, | ||
| currentDate: { getDayOfWeek: () => 3 }, | ||
| view: "week", | ||
| }, | ||
| { | ||
| user: {}, | ||
| defaultFirstDay: 1, | ||
| currentDate: { getDayOfWeek: () => 3 }, | ||
| view: "day", | ||
| }, | ||
| ], | ||
| }; | ||
|
|
||
| ``` | ||
| ### Eval output | ||
| (kind: ok) [{"getDayOfWeek":"[[ function params=0 ]]"},1,5] | ||
| [{"getDayOfWeek":"[[ function params=0 ]]"},3,1] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| // @flow @compilationMode:"infer" | ||
| 'use strict'; | ||
|
|
||
| function getWeekendDays(user) { | ||
| return [0, 6]; | ||
| } | ||
|
|
||
| function getConfig(weekendDays) { | ||
| return [1, 5]; | ||
| } | ||
|
|
||
| component Calendar(user, defaultFirstDay, currentDate, view) { | ||
| const weekendDays = getWeekendDays(user); | ||
| let firstDay = defaultFirstDay; | ||
| let daysToDisplay = 7; | ||
| if (view === 'week') { | ||
| let lastDay; | ||
| // this assignment produces invalid code | ||
| [firstDay, lastDay] = getConfig(weekendDays); | ||
| daysToDisplay = ((7 + lastDay - firstDay) % 7) + 1; | ||
| } else if (view === 'day') { | ||
| firstDay = currentDate.getDayOfWeek(); | ||
| daysToDisplay = 1; | ||
| } | ||
|
|
||
| return [currentDate, firstDay, daysToDisplay]; | ||
| } | ||
|
|
||
| export const FIXTURE_ENTRYPOINT = { | ||
| fn: Calendar, | ||
| params: [ | ||
| { | ||
| user: {}, | ||
| defaultFirstDay: 1, | ||
| currentDate: {getDayOfWeek: () => 3}, | ||
| view: 'week', | ||
| }, | ||
| ], | ||
| sequentialRenders: [ | ||
| { | ||
| user: {}, | ||
| defaultFirstDay: 1, | ||
| currentDate: {getDayOfWeek: () => 3}, | ||
| view: 'week', | ||
| }, | ||
| { | ||
| user: {}, | ||
| defaultFirstDay: 1, | ||
| currentDate: {getDayOfWeek: () => 3}, | ||
| view: 'day', | ||
| }, | ||
| ], | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the logic for
hasDeclared()here doesn't account for all local variables, so it was incorrectly reporting that the fixed destructuring had mixed reassignment and declarationsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see -- we add to declarations only when we see scope declarations, but this case has a local (with-scope) reassignment