diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index ecc22365dd..519c590611 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -83,6 +83,7 @@ export function lower( identifier: builder.resolveBinding(ref), effect: Effect.Unknown, reactive: false, + type: makeType(), loc: ref.loc ?? GeneratedSource, }); } @@ -114,6 +115,7 @@ export function lower( identifier: binding.identifier, effect: Effect.Unknown, reactive: false, + type: makeType(), loc: param.node.loc ?? GeneratedSource, }; params.push(place); @@ -127,6 +129,7 @@ export function lower( identifier: builder.makeTemporary(param.node.loc ?? GeneratedSource), effect: Effect.Unknown, reactive: false, + type: makeType(), loc: param.node.loc ?? GeneratedSource, }; promoteTemporary(place.identifier); @@ -145,6 +148,7 @@ export function lower( identifier: builder.makeTemporary(param.node.loc ?? GeneratedSource), effect: Effect.Unknown, reactive: false, + type: makeType(), loc: param.node.loc ?? GeneratedSource, }; params.push({ @@ -463,6 +467,7 @@ function lowerStatement( identifier: identifier.identifier, kind: 'Identifier', reactive: false, + type: makeType(), loc: id.node.loc ?? GeneratedSource, }; lowerValueToTemporary(builder, { @@ -856,6 +861,7 @@ function lowerStatement( identifier: binding.identifier, kind: 'Identifier', reactive: false, + type: makeType(), loc: id.node.loc ?? GeneratedSource, }; if (builder.isContextIdentifier(id)) { @@ -1265,6 +1271,7 @@ function lowerStatement( ), effect: Effect.Unknown, reactive: false, + type: makeType(), loc: handlerBindingPath.node.loc ?? GeneratedSource, }; promoteTemporary(place.identifier); @@ -3435,6 +3442,7 @@ function lowerIdentifier( identifier: binding.identifier, effect: Effect.Unknown, reactive: false, + type: makeType(), loc: exprLoc, }; return place; @@ -3456,6 +3464,7 @@ function buildTemporaryPlace(builder: HIRBuilder, loc: SourceLocation): Place { identifier: builder.makeTemporary(loc), effect: Effect.Unknown, reactive: false, + type: makeType(), loc, }; return place; @@ -3518,6 +3527,7 @@ function lowerIdentifierForAssignment( identifier: binding.identifier, effect: Effect.Unknown, reactive: false, + type: makeType(), loc, }; return place; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts index d3c919a6d8..4180691417 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts @@ -18,6 +18,7 @@ import { IdentifierId, InstructionId, InstructionValue, + makeType, ReactiveScopeDependency, ScopeId, } from './HIR'; @@ -216,6 +217,7 @@ class PropertyPathRegistry { optionalProperties: new Map(), fullPath: { identifier, + type: makeType(), path: [], }, hasOptional: false, @@ -239,6 +241,7 @@ class PropertyPathRegistry { parent: parent, fullPath: { identifier: parent.fullPath.identifier, + type: makeType(), path: parent.fullPath.path.concat(entry), }, hasOptional: parent.hasOptional || entry.optional, @@ -280,6 +283,7 @@ function getMaybeNonNullInInstruction( if (instr.kind === 'PropertyLoad') { path = context.temporaries.get(instr.object.identifier.id) ?? { identifier: instr.object.identifier, + type: makeType(), path: [], }; } else if (instr.kind === 'Destructure') { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts index 2b7c9f2134..565353f922 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts @@ -16,6 +16,7 @@ import { DependencyPathEntry, Instruction, Terminal, + makeType, } from './HIR'; import {printIdentifier} from './PrintHIR'; @@ -282,6 +283,7 @@ function traverseOptionalBlock( ); baseObject = { identifier: maybeTest.instructions[0].value.place.identifier, + type: maybeTest.instructions[0].value.place.type, path, }; test = maybeTest.terminal; @@ -383,6 +385,7 @@ function traverseOptionalBlock( ); const load = { identifier: baseObject.identifier, + type: makeType(), path: [ ...baseObject.path, { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/DeriveMinimalDependenciesHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/DeriveMinimalDependenciesHIR.ts index f5567b3e53..bbfd11c503 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/DeriveMinimalDependenciesHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/DeriveMinimalDependenciesHIR.ts @@ -10,6 +10,7 @@ import { DependencyPathEntry, GeneratedSource, Identifier, + makeType, ReactiveScopeDependency, } from '../HIR'; import {printIdentifier} from '../HIR/PrintHIR'; @@ -308,7 +309,7 @@ function collectMinimalDependenciesInSubtree( results: Set, ): void { if (isDependency(node.accessType)) { - results.add({identifier: rootIdentifier, path}); + results.add({identifier: rootIdentifier, type: makeType(), path}); } else { for (const [childName, childNode] of node.properties) { collectMinimalDependenciesInSubtree( diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index 954fb6f400..d7f6422966 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -11,7 +11,7 @@ import {CompilerError, CompilerErrorDetailOptions} from '../CompilerError'; import {assertExhaustive} from '../Utils/utils'; import {Environment, ReactFunctionType} from './Environment'; import {HookKind} from './ObjectShape'; -import {Type, makeType} from './Types'; +import {Type} from './Types'; import {z} from 'zod'; /* @@ -1108,6 +1108,7 @@ export type Place = { identifier: Identifier; effect: Effect; reactive: boolean; + type: Type; loc: SourceLocation; }; @@ -1211,7 +1212,6 @@ export type Identifier = { * variables may have the same scope id. */ scope: ReactiveScope | null; - type: Type; loc: SourceLocation; }; @@ -1238,7 +1238,6 @@ export function makeTemporaryIdentifier( declarationId: makeDeclarationId(id), mutableRange: {start: makeInstructionId(0), end: makeInstructionId(0)}, scope: null, - type: makeType(), loc, }; } @@ -1508,6 +1507,7 @@ export type ReactiveScopeDependencies = Set; export type ReactiveScopeDeclaration = { identifier: Identifier; + type: Type; scope: ReactiveScope; // the scope in which the variable was originally declared }; @@ -1515,6 +1515,7 @@ export type DependencyPathEntry = {property: string; optional: boolean}; export type DependencyPath = Array; export type ReactiveScopeDependency = { identifier: Identifier; + type: Type; path: DependencyPath; }; @@ -1628,110 +1629,92 @@ export function makeInstructionId(id: number): InstructionId { return id as InstructionId; } -export function isObjectMethodType(id: Identifier): boolean { - return id.type.kind == 'ObjectMethod'; +export function isObjectMethodType(type: Type): boolean { + return type.kind == 'ObjectMethod'; } -export function isObjectType(id: Identifier): boolean { - return id.type.kind === 'Object'; +export function isObjectType(type: Type): boolean { + return type.kind === 'Object'; } -export function isPrimitiveType(id: Identifier): boolean { - return id.type.kind === 'Primitive'; +export function isPrimitiveType(type: Type): boolean { + return type.kind === 'Primitive'; } -export function isArrayType(id: Identifier): boolean { - return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInArray'; +export function isArrayType(type: Type): boolean { + return type.kind === 'Object' && type.shapeId === 'BuiltInArray'; } -export function isRefValueType(id: Identifier): boolean { - return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInRefValue'; +export function isRefValueType(type: Type): boolean { + return type.kind === 'Object' && type.shapeId === 'BuiltInRefValue'; } -export function isUseRefType(id: Identifier): boolean { - return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseRefId'; +export function isUseRefType(type: Type): boolean { + return type.kind === 'Object' && type.shapeId === 'BuiltInUseRefId'; } -export function isUseStateType(id: Identifier): boolean { - return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseState'; +export function isUseStateType(type: Type): boolean { + return type.kind === 'Object' && type.shapeId === 'BuiltInUseState'; } -export function isRefOrRefValue(id: Identifier): boolean { - return isUseRefType(id) || isRefValueType(id); +export function isRefOrRefValue(type: Type): boolean { + return isUseRefType(type) || isRefValueType(type); } -export function isSetStateType(id: Identifier): boolean { - return id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetState'; +export function isSetStateType(type: Type): boolean { + return type.kind === 'Function' && type.shapeId === 'BuiltInSetState'; } -export function isUseActionStateType(id: Identifier): boolean { +export function isUseActionStateType(type: Type): boolean { + return type.kind === 'Object' && type.shapeId === 'BuiltInUseActionState'; +} + +export function isStartTransitionType(type: Type): boolean { + return type.kind === 'Function' && type.shapeId === 'BuiltInStartTransition'; +} + +export function isSetActionStateType(type: Type): boolean { + return type.kind === 'Function' && type.shapeId === 'BuiltInSetActionState'; +} + +export function isUseReducerType(type: Type): boolean { + return type.kind === 'Function' && type.shapeId === 'BuiltInUseReducer'; +} + +export function isDispatcherType(type: Type): boolean { + return type.kind === 'Function' && type.shapeId === 'BuiltInDispatch'; +} + +export function isStableType(type: Type): boolean { return ( - id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseActionState' + isSetStateType(type) || + isSetActionStateType(type) || + isDispatcherType(type) || + isUseRefType(type) || + isStartTransitionType(type) ); } -export function isStartTransitionType(id: Identifier): boolean { +export function isUseEffectHookType(type: Type): boolean { + return type.kind === 'Function' && type.shapeId === 'BuiltInUseEffectHook'; +} +export function isUseLayoutEffectHookType(type: Type): boolean { return ( - id.type.kind === 'Function' && id.type.shapeId === 'BuiltInStartTransition' + type.kind === 'Function' && type.shapeId === 'BuiltInUseLayoutEffectHook' + ); +} +export function isUseInsertionEffectHookType(type: Type): boolean { + return ( + type.kind === 'Function' && type.shapeId === 'BuiltInUseInsertionEffectHook' ); } -export function isSetActionStateType(id: Identifier): boolean { - return ( - id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetActionState' - ); +export function isUseContextHookType(type: Type): boolean { + return type.kind === 'Function' && type.shapeId === 'BuiltInUseContextHook'; } -export function isUseReducerType(id: Identifier): boolean { - return id.type.kind === 'Function' && id.type.shapeId === 'BuiltInUseReducer'; -} - -export function isDispatcherType(id: Identifier): boolean { - return id.type.kind === 'Function' && id.type.shapeId === 'BuiltInDispatch'; -} - -export function isStableType(id: Identifier): boolean { - return ( - isSetStateType(id) || - isSetActionStateType(id) || - isDispatcherType(id) || - isUseRefType(id) || - isStartTransitionType(id) - ); -} - -export function isUseEffectHookType(id: Identifier): boolean { - return ( - id.type.kind === 'Function' && id.type.shapeId === 'BuiltInUseEffectHook' - ); -} -export function isUseLayoutEffectHookType(id: Identifier): boolean { - return ( - id.type.kind === 'Function' && - id.type.shapeId === 'BuiltInUseLayoutEffectHook' - ); -} -export function isUseInsertionEffectHookType(id: Identifier): boolean { - return ( - id.type.kind === 'Function' && - id.type.shapeId === 'BuiltInUseInsertionEffectHook' - ); -} - -export function isUseContextHookType(id: Identifier): boolean { - return ( - id.type.kind === 'Function' && id.type.shapeId === 'BuiltInUseContextHook' - ); -} - -export function getHookKind(env: Environment, id: Identifier): HookKind | null { - return getHookKindForType(env, id.type); -} - -export function isUseOperator(id: Identifier): boolean { - return ( - id.type.kind === 'Function' && id.type.shapeId === 'BuiltInUseOperator' - ); +export function isUseOperator(type: Type): boolean { + return type.kind === 'Function' && type.shapeId === 'BuiltInUseOperator'; } export function getHookKindForType( @@ -1745,4 +1728,12 @@ export function getHookKindForType( return null; } +export function isEffectHook(type: Type): boolean { + return ( + isUseEffectHookType(type) || + isUseLayoutEffectHookType(type) || + isUseInsertionEffectHookType(type) + ); +} + export * from './Types'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts index 9202f2145f..0bbbd529f4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts @@ -328,7 +328,6 @@ export default class HIRBuilder { end: makeInstructionId(0), }, scope: null, - type: makeType(), loc: node.loc ?? GeneratedSource, }; this.#bindings.set(name, {node, identifier}); @@ -896,6 +895,7 @@ export function createTemporaryPlace( identifier: makeTemporaryIdentifier(env.nextIdentifierId, loc), reactive: false, effect: Effect.Unknown, + type: makeType(), loc: GeneratedSource, }; } @@ -908,7 +908,7 @@ export function createTemporaryPlace( export function clonePlaceToTemporary(env: Environment, place: Place): Place { const temp = createTemporaryPlace(env, place.loc); temp.effect = place.effect; - temp.identifier.type = place.identifier.type; + temp.type = place.type; temp.reactive = place.reactive; return temp; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/MergeConsecutiveBlocks.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/MergeConsecutiveBlocks.ts index ea132b772a..8c5bb1e7e2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/MergeConsecutiveBlocks.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/MergeConsecutiveBlocks.ts @@ -87,6 +87,7 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void { identifier: phi.place.identifier, effect: Effect.ConditionallyMutate, reactive: false, + type: phi.place.type, loc: GeneratedSource, }, value: { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts index 526ab7c7e5..aec8ee288d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts @@ -165,7 +165,7 @@ export function printPhi(phi: Phi): string { const items = []; items.push(printPlace(phi.place)); items.push(printMutableRange(phi.place.identifier)); - items.push(printType(phi.place.identifier.type)); + items.push(printType(phi.place.type)); items.push(': phi('); const phis = []; for (const [blockId, place] of phi.operands) { @@ -837,7 +837,7 @@ export function printPlace(place: Place): string { ' ', printIdentifier(place.identifier), printMutableRange(place.identifier), - printType(place.identifier.type), + printType(place.type), place.reactive ? '{reactive}' : null, ]; return items.filter(x => x != null).join(''); diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts index 8aed17f8ee..371f8a33e7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts @@ -17,6 +17,7 @@ import { areEqualPaths, IdentifierId, Terminal, + makeType, } from './HIR'; import { collectHoistablePropertyLoads, @@ -278,6 +279,7 @@ function collectTemporariesSidemapImpl( ) { temporaries.set(lvalue.identifier.id, { identifier: value.place.identifier, + type: value.place.type, path: [], }); } @@ -331,11 +333,13 @@ function getProperty( if (resolvedDependency == null) { property = { identifier: object.identifier, + type: makeType(), path: [{property: propertyName, optional}], }; } else { property = { identifier: resolvedDependency.identifier, + type: makeType(), path: [...resolvedDependency.path, {property: propertyName, optional}], }; } @@ -441,7 +445,7 @@ class Context { // Checks if identifier is a valid dependency in the current scope #checkValidDependency(maybeDependency: ReactiveScopeDependency): boolean { // ref value is not a valid dep - if (isRefValueType(maybeDependency.identifier)) { + if (isRefValueType(maybeDependency.type)) { return false; } @@ -449,7 +453,7 @@ class Context { * object methods are not deps because they will be codegen'ed back in to * the object literal. */ - if (isObjectMethodType(maybeDependency.identifier)) { + if (isObjectMethodType(maybeDependency.type)) { return false; } @@ -488,6 +492,7 @@ class Context { this.visitDependency( this.#temporaries.get(place.identifier.id) ?? { identifier: place.identifier, + type: place.type, path: [], }, ); @@ -535,6 +540,7 @@ class Context { ) { scope.declarations.set(maybeDependency.identifier.id, { identifier: maybeDependency.identifier, + type: maybeDependency.type, scope: originalDeclaration.scope.value!, }); } @@ -543,11 +549,12 @@ class Context { // ref.current access is not a valid dep if ( - isUseRefType(maybeDependency.identifier) && + isUseRefType(maybeDependency.type) && maybeDependency.path.at(0)?.property === 'current' ) { maybeDependency = { identifier: maybeDependency.identifier, + type: maybeDependency.type, path: [], }; } @@ -569,7 +576,11 @@ class Context { identifier => identifier.declarationId === place.identifier.declarationId, ) && - this.#checkValidDependency({identifier: place.identifier, path: []}) + this.#checkValidDependency({ + identifier: place.identifier, + type: place.type, + path: [], + }) ) { currentScope.reassignments.add(place.identifier); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts index 684acaf298..e4cb91077e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts @@ -140,7 +140,7 @@ function infer( name = dep.identifier.name; } - if (isRefOrRefValue(dep.identifier)) { + if (isRefOrRefValue(dep.type)) { /* * TODO: this is a hack to ensure we treat functions which reference refs * as having a capture and therefore being considered mutable. this ensures diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts index 4dcdc21e15..208381d7f7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts @@ -28,6 +28,7 @@ import { TInstruction, getHookKindForType, makeInstructionId, + makeType, } from '../HIR'; import {createTemporaryPlace, markInstructionIds} from '../HIR/HIRBuilder'; @@ -269,6 +270,7 @@ function getManualMemoizationReplacement( identifier: fn.identifier, effect: Effect.Unknown, reactive: false, + type: makeType(), loc, }, loc, @@ -421,6 +423,7 @@ export function dropManualMemoization(func: HIRFunction): void { identifier: fnPlace.identifier, effect: Effect.Unknown, reactive: false, + type: makeType(), loc: fnPlace.loc, }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAlias.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAlias.ts index 80422c8391..e939686b03 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAlias.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAlias.ts @@ -5,64 +5,136 @@ * LICENSE file in the root directory of this source tree. */ +import invariant from 'invariant'; +import {CompilerError} from '..'; import { HIRFunction, Identifier, + IdentifierId, Instruction, isPrimitiveType, Place, } from '../HIR/HIR'; +import {printPlace} from '../HIR/PrintHIR'; +import { + eachInstructionLValue, + eachInstructionValueOperand, + eachPatternOperand, + eachTerminalOperand, +} from '../HIR/visitors'; import DisjointSet from '../Utils/DisjointSet'; export type AliasSet = Set; -export function inferAliases(func: HIRFunction): DisjointSet { - const aliases = new DisjointSet(); +export function inferAliases(func: HIRFunction): DisjointSet { + const aliases = new DisjointSet(); + const declarations = new Map(); + for (const param of func.params) { + const place = param.kind === 'Identifier' ? param : param.place; + declarations.set(place.identifier.id, place); + } for (const [_, block] of func.body.blocks) { + for (const phi of block.phis) { + declarations.set(phi.place.identifier.id, phi.place); + } for (const instr of block.instructions) { - inferInstr(instr, aliases); + inferInstr(instr, aliases, declarations); + } + for (const operand of eachTerminalOperand(block.terminal)) { + const declaration = declarations.get(operand.identifier.id); + if (declaration !== undefined) { + aliases.union([declaration, operand]); + } } } return aliases; } +function assertGet(map: Map, place: Place): Place { + const value = map.get(place.identifier.id); + if (value === undefined) { + CompilerError.invariant(value !== undefined, { + reason: `Missing declaration for ${printPlace(place)}`, + loc: place.loc, + }); + } + return value; +} + function inferInstr( instr: Instruction, - aliases: DisjointSet, + aliases: DisjointSet, + declarations: Map, ): void { const {lvalue, value: instrValue} = instr; - let alias: Place | null = null; switch (instrValue.kind) { case 'LoadLocal': case 'LoadContext': { - if (isPrimitiveType(instrValue.place.identifier)) { - return; + if (!isPrimitiveType(instrValue.place.type)) { + const places = [ + lvalue, + instrValue.place, + assertGet(declarations, instrValue.place), + ]; + aliases.union(places); } - alias = instrValue.place; break; } case 'StoreLocal': case 'StoreContext': { - alias = instrValue.value; + if (!isPrimitiveType(instrValue.value.type)) { + const places = [ + lvalue, + instrValue.lvalue.place, + instrValue.value, + assertGet(declarations, instrValue.value)!, + ]; + aliases.union(places); + } break; } case 'Destructure': { - alias = instrValue.value; + aliases.union([ + lvalue, + ...eachPatternOperand(instrValue.lvalue.pattern), + instrValue.value, + assertGet(declarations, instrValue.value)!, + ]); break; } case 'ComputedLoad': case 'PropertyLoad': { - alias = instrValue.object; + aliases.union([ + lvalue, + instrValue.object, + assertGet(declarations, instrValue.object)!, + ]); break; } case 'TypeCastExpression': { - alias = instrValue.value; + if (!isPrimitiveType(instrValue.value.type)) { + aliases.union([ + lvalue, + instrValue.value, + assertGet(declarations, instrValue.value), + ]); + } + break; + } + default: { + for (const operand of eachInstructionValueOperand(instrValue)) { + const declaration = declarations.get(operand.identifier.id); + if (declaration != null) { + aliases.union([operand, declaration]); + } + } break; } - default: - return; } - - aliases.union([lvalue.identifier, alias.identifier]); + for (const lvalue of eachInstructionLValue(instr)) { + if (!declarations.has(lvalue.identifier.id)) { + declarations.set(lvalue.identifier.id, lvalue); + } + } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAliasForPhis.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAliasForPhis.ts index e81e3ebdae..a77a7e9819 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAliasForPhis.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAliasForPhis.ts @@ -5,12 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import {HIRFunction, Identifier} from '../HIR/HIR'; +import {HIRFunction, Place} from '../HIR/HIR'; import DisjointSet from '../Utils/DisjointSet'; export function inferAliasForPhis( func: HIRFunction, - aliases: DisjointSet, + aliases: DisjointSet, ): void { for (const [_, block] of func.body.blocks) { for (const phi of block.phis) { @@ -19,7 +19,7 @@ export function inferAliasForPhis( (block.instructions.at(0)?.id ?? block.terminal.id); if (isPhiMutatedAfterCreation) { for (const [, operand] of phi.operands) { - aliases.union([phi.place.identifier, operand.identifier]); + aliases.union([phi.place, operand]); } } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAliasForStores.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAliasForStores.ts index d8d17f6a5f..bd61d91995 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAliasForStores.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferAliasForStores.ts @@ -5,13 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import { - Effect, - HIRFunction, - Identifier, - InstructionId, - Place, -} from '../HIR/HIR'; +import {Effect, HIRFunction, InstructionId, Place} from '../HIR/HIR'; import { eachInstructionLValue, eachInstructionValueOperand, @@ -20,7 +14,7 @@ import DisjointSet from '../Utils/DisjointSet'; export function inferAliasForStores( func: HIRFunction, - aliases: DisjointSet, + aliases: DisjointSet, ): void { for (const [_, block] of func.body.blocks) { for (const instr of block.instructions) { @@ -54,7 +48,7 @@ export function inferAliasForStores( } function maybeAlias( - aliases: DisjointSet, + aliases: DisjointSet, lvalue: Place, rvalue: Place, id: InstructionId, @@ -63,6 +57,6 @@ function maybeAlias( lvalue.identifier.mutableRange.end > id + 1 || rvalue.identifier.mutableRange.end > id ) { - aliases.union([lvalue.identifier, rvalue.identifier]); + aliases.union([lvalue, rvalue]); } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferFunctionEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferFunctionEffects.ts index 0ae54839b6..c25117dcea 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferFunctionEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferFunctionEffects.ts @@ -16,7 +16,7 @@ import { InstructionValue, Place, ValueReason, - getHookKind, + getHookKindForType, isRefOrRefValue, } from '../HIR'; import {eachInstructionOperand, eachTerminalOperand} from '../HIR/visitors'; @@ -38,7 +38,7 @@ function inferOperandEffect(state: State, place: Place): null | FunctionEffect { switch (place.effect) { case Effect.Store: case Effect.Mutate: { - if (isRefOrRefValue(place.identifier)) { + if (isRefOrRefValue(place.type)) { break; } else if (value.kind === ValueKind.Context) { return { @@ -235,7 +235,7 @@ export function inferInstructionFunctionEffects( callee = instr.value.callee; } functionEffects.push(...operandEffects(state, callee, false)); - let isHook = getHookKind(env, callee.identifier) != null; + let isHook = getHookKindForType(env, callee.type) != null; for (const arg of instr.value.args) { const place = arg.kind === 'Identifier' ? arg : arg.place; /* diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableLifetimes.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableLifetimes.ts index 508a970d93..dcdf7e6e95 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableLifetimes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableLifetimes.ts @@ -67,7 +67,7 @@ import {assertExhaustive} from '../Utils/utils'; */ function infer(place: Place, instrId: InstructionId): void { - if (!isRefOrRefValue(place.identifier)) { + if (!isRefOrRefValue(place.type)) { place.identifier.mutableRange.end = makeInstructionId(instrId + 1); } } @@ -180,7 +180,7 @@ export function inferMutableLifetimes( ); if ( declaration != null && - !isRefOrRefValue(instr.value.lvalue.place.identifier) + !isRefOrRefValue(instr.value.lvalue.place.type) ) { const range = instr.value.lvalue.place.identifier.mutableRange; if (range.start === 0) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableRanges.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableRanges.ts index a8f33b31d5..1ef137f5b0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableRanges.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableRanges.ts @@ -5,13 +5,16 @@ * LICENSE file in the root directory of this source tree. */ -import {HIRFunction, Identifier} from '../HIR/HIR'; +import prettyFormat from 'pretty-format'; +import {HIRFunction, Place} from '../HIR/HIR'; import {inferAliases} from './InferAlias'; import {inferAliasForPhis} from './InferAliasForPhis'; import {inferAliasForStores} from './InferAliasForStores'; import {inferMutableLifetimes} from './InferMutableLifetimes'; import {inferMutableRangesForAlias} from './InferMutableRangesForAlias'; import {inferTryCatchAliases} from './InferTryCatchAliases'; +import {printIdentifier, printPlace} from '../HIR/PrintHIR'; +import {getOrInsertDefault} from '../Utils/utils'; export function inferMutableRanges(ir: HIRFunction): void { // Infer mutable ranges for non fields @@ -19,6 +22,13 @@ export function inferMutableRanges(ir: HIRFunction): void { // Calculate aliases const aliases = inferAliases(ir); + // const a = aliases.canonicalize(); + // const f = new Map(); + // for (const [k, v] of a) { + // getOrInsertDefault(f, printPlace(v), []).push(printPlace(k)); + // } + // console.log(prettyFormat(f)); + /* * Calculate aliases for try/catch, where any value created * in the try block could be aliased to the catch param @@ -29,7 +39,7 @@ export function inferMutableRanges(ir: HIRFunction): void { * Eagerly canonicalize so that if nothing changes we can bail out * after a single iteration */ - let prevAliases: Map = aliases.canonicalize(); + let prevAliases: Map = aliases.canonicalize(); while (true) { // Infer mutable ranges for aliases that are not fields inferMutableRangesForAlias(ir, aliases); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableRangesForAlias.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableRangesForAlias.ts index a7e8b5c1f7..528db62c79 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableRangesForAlias.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableRangesForAlias.ts @@ -5,17 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import { - HIRFunction, - Identifier, - InstructionId, - isRefOrRefValue, -} from '../HIR/HIR'; +import {HIRFunction, InstructionId, isRefOrRefValue, Place} from '../HIR/HIR'; import DisjointSet from '../Utils/DisjointSet'; export function inferMutableRangesForAlias( _fn: HIRFunction, - aliases: DisjointSet, + aliases: DisjointSet, ): void { const aliasSets = aliases.buildSets(); for (const aliasSet of aliasSets) { @@ -24,16 +19,18 @@ export function inferMutableRangesForAlias( * mutated. */ const mutatingIdentifiers = [...aliasSet].filter( - id => - id.mutableRange.end - id.mutableRange.start > 1 && !isRefOrRefValue(id), + place => + place.identifier.mutableRange.end - + place.identifier.mutableRange.start > + 1 && !isRefOrRefValue(place.type), ); if (mutatingIdentifiers.length > 0) { // Find final instruction which mutates this alias set. let lastMutatingInstructionId = 0; - for (const id of mutatingIdentifiers) { - if (id.mutableRange.end > lastMutatingInstructionId) { - lastMutatingInstructionId = id.mutableRange.end; + for (const place of mutatingIdentifiers) { + if (place.identifier.mutableRange.end > lastMutatingInstructionId) { + lastMutatingInstructionId = place.identifier.mutableRange.end; } } @@ -43,10 +40,11 @@ export function inferMutableRangesForAlias( */ for (const alias of aliasSet) { if ( - alias.mutableRange.end < lastMutatingInstructionId && - !isRefOrRefValue(alias) + alias.identifier.mutableRange.end < lastMutatingInstructionId && + !isRefOrRefValue(alias.type) ) { - alias.mutableRange.end = lastMutatingInstructionId as InstructionId; + alias.identifier.mutableRange.end = + lastMutatingInstructionId as InstructionId; } } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReactivePlaces.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReactivePlaces.ts index 344949b020..dba25b5028 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReactivePlaces.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReactivePlaces.ts @@ -14,7 +14,7 @@ import { IdentifierId, Place, computePostDominatorTree, - getHookKind, + getHookKindForType, isStableType, isUseOperator, } from '../HIR'; @@ -204,21 +204,21 @@ export function inferReactivePlaces(fn: HIRFunction): void { */ if ( value.kind === 'CallExpression' && - (getHookKind(fn.env, value.callee.identifier) != null || - isUseOperator(value.callee.identifier)) + (getHookKindForType(fn.env, value.callee.type) != null || + isUseOperator(value.callee.type)) ) { hasReactiveInput = true; } else if ( value.kind === 'MethodCall' && - (getHookKind(fn.env, value.property.identifier) != null || - isUseOperator(value.property.identifier)) + (getHookKindForType(fn.env, value.property.type) != null || + isUseOperator(value.property.type)) ) { hasReactiveInput = true; } if (hasReactiveInput) { for (const lvalue of eachInstructionLValue(instruction)) { - if (isStableType(lvalue.identifier)) { + if (isStableType(lvalue.type)) { continue; } reactiveIdentifiers.markReactive(lvalue); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index 8cf30a9666..04fb0f1428 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -478,7 +478,7 @@ class InferenceState { * `expected valueKind to be 'Mutable' but found to be \`${valueKind}\`` * ); */ - effect = isObjectType(place.identifier) ? Effect.Store : Effect.Mutate; + effect = isObjectType(place.type) ? Effect.Store : Effect.Mutate; break; } case Effect.Capture: { @@ -1175,10 +1175,7 @@ function inferBlock( loc: instrValue.loc, }); } - const signature = getFunctionCallSignature( - env, - instrValue.tag.identifier.type, - ); + const signature = getFunctionCallSignature(env, instrValue.tag.type); let calleeEffect = signature?.calleeEffect ?? Effect.ConditionallyMutate; const returnValueKind: AbstractValue = @@ -1209,10 +1206,7 @@ function inferBlock( break; } case 'CallExpression': { - const signature = getFunctionCallSignature( - env, - instrValue.callee.identifier.type, - ); + const signature = getFunctionCallSignature(env, instrValue.callee.type); const effects = signature !== null ? getFunctionEffects(instrValue, signature) : null; @@ -1294,7 +1288,7 @@ function inferBlock( const signature = getFunctionCallSignature( env, - instrValue.property.identifier.type, + instrValue.property.type, ); const returnValueKind: AbstractValue = @@ -1797,7 +1791,7 @@ function inferBlock( kind === ValueKind.Mutable || kind === ValueKind.Context; let effect; let valueKind: AbstractValue; - if (!isMutable || isArrayType(instrValue.collection.identifier)) { + if (!isMutable || isArrayType(instrValue.collection.type)) { // Case 1, assume iterator is a separate mutable object effect = { kind: Effect.Read, diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferTryCatchAliases.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferTryCatchAliases.ts index 3b33160820..dd32cbcc30 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferTryCatchAliases.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferTryCatchAliases.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {BlockId, HIRFunction, Identifier} from '../HIR'; +import {BlockId, HIRFunction, Place} from '../HIR'; import DisjointSet from '../Utils/DisjointSet'; /* @@ -16,18 +16,15 @@ import DisjointSet from '../Utils/DisjointSet'; */ export function inferTryCatchAliases( fn: HIRFunction, - aliases: DisjointSet, + aliases: DisjointSet, ): void { - const handlerParams: Map = new Map(); + const handlerParams: Map = new Map(); for (const [_, block] of fn.body.blocks) { if ( block.terminal.kind === 'try' && block.terminal.handlerBinding !== null ) { - handlerParams.set( - block.terminal.handler, - block.terminal.handlerBinding.identifier, - ); + handlerParams.set(block.terminal.handler, block.terminal.handlerBinding); } else if (block.terminal.kind === 'maybe-throw') { const handlerParam = handlerParams.get(block.terminal.handler); if (handlerParam === undefined) { @@ -42,7 +39,7 @@ export function inferTryCatchAliases( * catch clause param */ for (const instr of block.instructions) { - aliases.union([handlerParam, instr.lvalue.identifier]); + aliases.union([handlerParam, instr.lvalue]); } } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts index d97a4da1ec..c6a05ead1c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts @@ -402,6 +402,7 @@ export function inlineJsxTransform( scope.declarations.delete(origId); scope.declarations.set(decl.identifier.id, { identifier: newDecl, + type: decl.type, scope: decl.scope, }); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts index e27b8f9521..34c4d20f0e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts @@ -46,7 +46,7 @@ export function lowerContextAccess( if ( value.kind === 'CallExpression' && - isUseContextHookType(value.callee.identifier) + isUseContextHookType(value.callee.type) ) { contextAccess.set(lvalue.identifier.id, value); continue; @@ -87,7 +87,7 @@ export function lowerContextAccess( const {lvalue, value} = instr; if ( value.kind === 'CallExpression' && - isUseContextHookType(value.callee.identifier) && + isUseContextHookType(value.callee.type) && contextKeys.has(lvalue.identifier.id) ) { const loweredContextCalleeInstr = emitLoadLoweredContextCallee( diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index 167db6dede..f43aaac12a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -41,7 +41,7 @@ import { SourceLocation, SpreadPattern, ValidIdentifierName, - getHookKind, + getHookKindForType, makeIdentifierName, } from '../HIR/HIR'; import {printIdentifier, printPlace} from '../HIR/PrintHIR'; @@ -1687,7 +1687,7 @@ function codegenInstructionValue( } break; } - const isHook = getHookKind(cx.env, instrValue.callee.identifier) != null; + const isHook = getHookKindForType(cx.env, instrValue.callee.type) != null; const callee = codegenPlaceToExpression(cx, instrValue.callee); const args = instrValue.args.map(arg => codegenArgument(cx, arg)); value = createCallExpression( @@ -1751,7 +1751,7 @@ function codegenInstructionValue( } case 'MethodCall': { const isHook = - getHookKind(cx.env, instrValue.property.identifier) != null; + getHookKindForType(cx.env, instrValue.property.type) != null; const memberExpr = codegenPlaceToExpression(cx, instrValue.property); CompilerError.invariant( t.isMemberExpression(memberExpr) || diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CollectReactiveIdentifiers.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CollectReactiveIdentifiers.ts index 3851234005..52da20cb30 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CollectReactiveIdentifiers.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CollectReactiveIdentifiers.ts @@ -14,6 +14,7 @@ import { isPrimitiveType, isUseRefType, Identifier, + Type, } from '../HIR/HIR'; import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors'; @@ -53,8 +54,8 @@ class Visitor extends ReactiveFunctionVisitor> { for (const [id, decl] of scopeBlock.scope.declarations) { if ( - !isPrimitiveType(decl.identifier) && - !isStableRefType(decl.identifier, state) + !isPrimitiveType(decl.type) && + !isStableRefType(decl.type, decl.identifier, state) ) { state.add(id); } @@ -62,10 +63,11 @@ class Visitor extends ReactiveFunctionVisitor> { } } function isStableRefType( + type: Type, identifier: Identifier, reactiveIdentifiers: Set, ): boolean { - return isUseRefType(identifier) && !reactiveIdentifiers.has(identifier.id); + return isUseRefType(type) && !reactiveIdentifiers.has(identifier.id); } /* * Computes a set of identifiers which are reactive, using the analysis previously performed diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/DeriveMinimalDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/DeriveMinimalDependencies.ts index c7e16cce7a..59680cec80 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/DeriveMinimalDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/DeriveMinimalDependencies.ts @@ -6,7 +6,12 @@ */ import {CompilerError} from '../CompilerError'; -import {DependencyPath, Identifier, ReactiveScopeDependency} from '../HIR'; +import { + DependencyPath, + Identifier, + makeType, + ReactiveScopeDependency, +} from '../HIR'; import {printIdentifier} from '../HIR/PrintHIR'; import {assertExhaustive} from '../Utils/utils'; @@ -107,6 +112,7 @@ export class ReactiveScopeDependencyTree { for (const dep of deps) { results.add({ identifier: rootId, + type: makeType(), path: dep.relativePath, }); } @@ -121,7 +127,7 @@ export class ReactiveScopeDependencyTree { checkValidDepIdFn: (dep: ReactiveScopeDependency) => boolean, ): void { for (const [id, otherRoot] of depsFromInnerScope.#roots) { - if (!checkValidDepIdFn({identifier: id, path: []})) { + if (!checkValidDepIdFn({identifier: id, type: makeType(), path: []})) { continue; } let currRoot = this.#getOrCreateRoot(id); diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/FlattenScopesWithHooksOrUseHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/FlattenScopesWithHooksOrUseHIR.ts index 103923a2e4..5fe3f05237 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/FlattenScopesWithHooksOrUseHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/FlattenScopesWithHooksOrUseHIR.ts @@ -11,7 +11,7 @@ import { HIRFunction, LabelTerminal, PrunedScopeTerminal, - getHookKind, + getHookKindForType, isUseOperator, } from '../HIR'; import {retainWhere} from '../Utils/utils'; @@ -52,8 +52,8 @@ export function flattenScopesWithHooksOrUseHIR(fn: HIRFunction): void { const callee = value.kind === 'MethodCall' ? value.property : value.callee; if ( - getHookKind(fn.env, callee.identifier) != null || - isUseOperator(callee.identifier) + getHookKindForType(fn.env, callee.type) != null || + isUseOperator(callee.type) ) { prune.push(...activeScopes.map(entry => entry.block)); activeScopes.length = 0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts index 098139b150..e9d67b895a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts @@ -238,7 +238,7 @@ function mayAllocate(env: Environment, instruction: Instruction): boolean { case 'TaggedTemplateExpression': case 'CallExpression': case 'MethodCall': { - return instruction.lvalue.identifier.type.kind !== 'Primitive'; + return instruction.lvalue.type.kind !== 'Primitive'; } case 'RegExpLiteral': case 'PropertyStore': diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts index 08d2212d86..54e886619b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts @@ -456,6 +456,7 @@ function canMergeScopes( new Set( [...current.scope.declarations.values()].map(declaration => ({ identifier: declaration.identifier, + type: declaration.type, path: [], })), ), @@ -464,7 +465,7 @@ function canMergeScopes( (next.scope.dependencies.size !== 0 && [...next.scope.dependencies].every( dep => - isAlwaysInvalidatingType(dep.identifier.type) && + isAlwaysInvalidatingType(dep.type) && Iterable_some( current.scope.declarations.values(), decl => @@ -545,6 +546,6 @@ function scopeIsEligibleForMerging(scopeBlock: ReactiveScopeBlock): boolean { return true; } return [...scopeBlock.scope.declarations].some(([, decl]) => - isAlwaysInvalidatingType(decl.identifier.type), + isAlwaysInvalidatingType(decl.type), ); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction.ts index b5aa44ead0..4c0d4077c3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction.ts @@ -111,8 +111,7 @@ export function writePrunedScope( export function printDependency(dependency: ReactiveScopeDependency): string { const identifier = - printIdentifier(dependency.identifier) + - printType(dependency.identifier.type); + printIdentifier(dependency.identifier) + printType(dependency.type); return `${identifier}${dependency.path.map(token => `${token.optional ? '?.' : '.'}${token.property}`).join('')}`; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateEarlyReturns.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateEarlyReturns.ts index b8ba196284..d36199edc0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateEarlyReturns.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateEarlyReturns.ts @@ -17,6 +17,7 @@ import { ReactiveStatement, ReactiveTerminalStatement, makeInstructionId, + makeType, promoteTemporary, } from '../HIR'; import {createTemporaryPlace} from '../HIR/HIRBuilder'; @@ -154,6 +155,7 @@ class Transform extends ReactiveFunctionTransform { scopeBlock.scope.earlyReturnValue = earlyReturnValue; scopeBlock.scope.declarations.set(earlyReturnValue.value.id, { identifier: earlyReturnValue.value, + type: makeType(), scope: scopeBlock.scope, }); @@ -239,6 +241,7 @@ class Transform extends ReactiveFunctionTransform { effect: Effect.ConditionallyMutate, loc, reactive: true, + type: makeType(), identifier: earlyReturnValue.value, }, }, @@ -310,6 +313,7 @@ class Transform extends ReactiveFunctionTransform { effect: Effect.Capture, loc, reactive: true, + type: makeType(), }, }, value: stmt.terminal.value, diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneInitializationDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneInitializationDependencies.ts index 2a9d0b9793..bec42f98a2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneInitializationDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneInitializationDependencies.ts @@ -8,7 +8,6 @@ import {CompilerError} from '../CompilerError'; import { Environment, - Identifier, IdentifierId, InstructionId, Place, @@ -17,7 +16,8 @@ import { ReactiveInstruction, ReactiveScopeBlock, ReactiveTerminalStatement, - getHookKind, + Type, + getHookKindForType, isUseRefType, isUseStateType, } from '../HIR'; @@ -92,8 +92,8 @@ class Visitor extends ReactiveFunctionVisitor { return values.reduce(join2, 'Unknown'); } - isCreateOnlyHook(id: Identifier): boolean { - return isUseStateType(id) || isUseRefType(id); + isCreateOnlyHook(type: Type): boolean { + return isUseStateType(type) || isUseRefType(type); } override visitPlace( @@ -136,15 +136,17 @@ class Visitor extends ReactiveFunctionVisitor { let callee = null; switch (instruction.value.kind) { case 'CallExpression': { - callee = instruction.value.callee.identifier; + callee = instruction.value.callee; break; } case 'MethodCall': { - callee = instruction.value.property.identifier; + callee = instruction.value.property; break; } } - return callee != null && getHookKind(this.env, callee) != null; + return ( + callee != null && getHookKindForType(this.env, callee.type) != null + ); }; switch (instruction.value.kind) { @@ -152,7 +154,7 @@ class Visitor extends ReactiveFunctionVisitor { case 'MethodCall': { if ( instruction.lvalue && - this.isCreateOnlyHook(instruction.lvalue.identifier) + this.isCreateOnlyHook(instruction.lvalue.type) ) { [...eachCallArgument(instruction.value.args)].forEach(operand => this.visitPlace(instruction.id, operand, 'Create'), diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneNonEscapingScopes.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneNonEscapingScopes.ts index 5a9aa6b2a7..9fefc8b94a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneNonEscapingScopes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneNonEscapingScopes.ts @@ -21,12 +21,11 @@ import { ReactiveTerminalStatement, ReactiveValue, ScopeId, - getHookKind, isMutableEffect, } from '../HIR'; import {getFunctionCallSignature} from '../Inference/InferReferenceEffects'; import {assertExhaustive, getOrInsertDefault} from '../Utils/utils'; -import {getPlaceScope} from '../HIR/HIR'; +import {getHookKindForType, getPlaceScope} from '../HIR/HIR'; import { ReactiveFunctionTransform, ReactiveFunctionVisitor, @@ -672,10 +671,7 @@ function computeMemoizationInputs( }; } case 'TaggedTemplateExpression': { - const signature = getFunctionCallSignature( - env, - value.tag.identifier.type, - ); + const signature = getFunctionCallSignature(env, value.tag.type); let lvalues = []; if (lvalue !== null) { lvalues.push({place: lvalue, level: MemoizationLevel.Memoized}); @@ -698,10 +694,7 @@ function computeMemoizationInputs( }; } case 'CallExpression': { - const signature = getFunctionCallSignature( - env, - value.callee.identifier.type, - ); + const signature = getFunctionCallSignature(env, value.callee.type); let lvalues = []; if (lvalue !== null) { lvalues.push({place: lvalue, level: MemoizationLevel.Memoized}); @@ -724,10 +717,7 @@ function computeMemoizationInputs( }; } case 'MethodCall': { - const signature = getFunctionCallSignature( - env, - value.property.identifier.type, - ); + const signature = getFunctionCallSignature(env, value.property.type); let lvalues = []; if (lvalue !== null) { lvalues.push({place: lvalue, level: MemoizationLevel.Memoized}); @@ -922,11 +912,8 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor { instruction.value.kind === 'CallExpression' ? instruction.value.callee : instruction.value.property; - if (getHookKind(state.env, callee.identifier) != null) { - const signature = getFunctionCallSignature( - this.env, - callee.identifier.type, - ); + if (getHookKindForType(state.env, callee.type) != null) { + const signature = getFunctionCallSignature(this.env, callee.type); /* * Hook values are assumed to escape by default since they can be inputs * to reactive scopes in the hook. However if the hook is annotated as diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneNonReactiveDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneNonReactiveDependencies.ts index 9bdf15aeb5..d2c2198fdc 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneNonReactiveDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneNonReactiveDependencies.ts @@ -56,7 +56,7 @@ class Visitor extends ReactiveFunctionVisitor { case 'Destructure': { if (state.has(value.value.identifier.id)) { for (const lvalue of eachPatternOperand(value.lvalue.pattern)) { - if (isStableType(lvalue.identifier)) { + if (isStableType(lvalue.type)) { continue; } state.add(lvalue.identifier.id); @@ -71,7 +71,7 @@ class Visitor extends ReactiveFunctionVisitor { if ( lvalue !== null && state.has(value.object.identifier.id) && - !isStableType(lvalue.identifier) + !isStableType(lvalue.type) ) { state.add(lvalue.identifier.id); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/SSA/EnterSSA.ts b/compiler/packages/babel-plugin-react-compiler/src/SSA/EnterSSA.ts index caba0d3c36..02852cf2ba 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/SSA/EnterSSA.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/SSA/EnterSSA.ts @@ -14,7 +14,6 @@ import { Identifier, IdentifierId, makeInstructionId, - makeType, Phi, Place, } from '../HIR/HIR'; @@ -86,7 +85,6 @@ class SSABuilder { end: makeInstructionId(0), }, scope: null, // reset along w the mutable range - type: makeType(), loc: oldId.loc, }; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts index 0270723c3e..da58338e9e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts @@ -15,6 +15,7 @@ import { IdentifierId, Instruction, makeType, + Place, PropType, Type, typeEquals, @@ -69,17 +70,17 @@ export function inferTypes(func: HIRFunction): void { function apply(func: HIRFunction, unifier: Unifier): void { for (const [_, block] of func.body.blocks) { for (const phi of block.phis) { - phi.place.identifier.type = unifier.get(phi.place.identifier.type); + phi.place.type = unifier.get(phi.place.type); } for (const instr of block.instructions) { for (const operand of eachInstructionLValue(instr)) { - operand.identifier.type = unifier.get(operand.identifier.type); + operand.type = unifier.get(operand.type); } for (const place of eachInstructionOperand(instr)) { - place.identifier.type = unifier.get(place.identifier.type); + place.type = unifier.get(place.type); } const {lvalue, value} = instr; - lvalue.identifier.type = unifier.get(lvalue.identifier.type); + lvalue.type = unifier.get(lvalue.type); if ( value.kind === 'FunctionExpression' || @@ -107,16 +108,26 @@ function equation(left: Type, right: Type): TypeEquation { function* generate( func: HIRFunction, ): Generator { + const typeEnv = new Map(); + function getType(place: Place): Type { + let type = typeEnv.get(place.identifier.id); + if (type === undefined) { + type = place.type; + typeEnv.set(place.identifier.id, type); + } + return type; + } + if (func.fnType === 'Component') { const [props, ref] = func.params; if (props && props.kind === 'Identifier') { - yield equation(props.identifier.type, { + yield equation(getType(props), { kind: 'Object', shapeId: BuiltInPropsId, }); } if (ref && ref.kind === 'Identifier') { - yield equation(ref.identifier.type, { + yield equation(getType(ref), { kind: 'Object', shapeId: BuiltInUseRefId, }); @@ -127,18 +138,23 @@ function* generate( const returnTypes: Array = []; for (const [_, block] of func.body.blocks) { for (const phi of block.phis) { - yield equation(phi.place.identifier.type, { + yield equation(getType(phi.place), { kind: 'Phi', - operands: [...phi.operands.values()].map(id => id.identifier.type), + operands: [...phi.operands.values()].map(id => getType(id)), }); } for (const instr of block.instructions) { - yield* generateInstructionTypes(func.env, names, instr); + yield* generateInstructionTypes( + func.env, + names, + (place: Place) => getType(place), + instr, + ); } const terminal = block.terminal; if (terminal.kind === 'return') { - returnTypes.push(terminal.value.identifier.type); + returnTypes.push(getType(terminal.value)); } } if (returnTypes.length > 1) { @@ -168,10 +184,11 @@ function getName(names: Map, id: IdentifierId): string { function* generateInstructionTypes( env: Environment, names: Map, + getType: (place: Place) => Type, instr: Instruction, ): Generator { const {lvalue, value} = instr; - const left = lvalue.identifier.type; + const left = getType(lvalue); switch (value.kind) { case 'TemplateLiteral': @@ -188,7 +205,7 @@ function* generateInstructionTypes( case 'LoadLocal': { setName(names, lvalue.identifier.id, value.place.identifier); - yield equation(left, value.place.identifier.type); + yield equation(left, getType(value.place)); break; } @@ -201,33 +218,27 @@ function* generateInstructionTypes( case 'StoreLocal': { if (env.config.enableUseTypeAnnotations) { - yield equation( - value.lvalue.place.identifier.type, - value.value.identifier.type, - ); + yield equation(getType(value.lvalue.place), getType(value.value)); const valueType = value.type === null ? makeType() : lowerType(value.type); - yield equation(valueType, value.lvalue.place.identifier.type); + yield equation(valueType, getType(value.lvalue.place)); yield equation(left, valueType); } else { - yield equation(left, value.value.identifier.type); - yield equation( - value.lvalue.place.identifier.type, - value.value.identifier.type, - ); + yield equation(left, getType(value.value)); + yield equation(getType(value.lvalue.place), getType(value.value)); } break; } case 'StoreGlobal': { - yield equation(left, value.value.identifier.type); + yield equation(left, getType(value.value)); break; } case 'BinaryExpression': { if (isPrimitiveBinaryOp(value.operator)) { - yield equation(value.left.identifier.type, {kind: 'Primitive'}); - yield equation(value.right.identifier.type, {kind: 'Primitive'}); + yield equation(getType(value.left), {kind: 'Primitive'}); + yield equation(getType(value.right), {kind: 'Primitive'}); } yield equation(left, {kind: 'Primitive'}); break; @@ -235,8 +246,8 @@ function* generateInstructionTypes( case 'PostfixUpdate': case 'PrefixUpdate': { - yield equation(value.value.identifier.type, {kind: 'Primitive'}); - yield equation(value.lvalue.identifier.type, {kind: 'Primitive'}); + yield equation(getType(value.value), {kind: 'Primitive'}); + yield equation(getType(value.lvalue), {kind: 'Primitive'}); yield equation(left, {kind: 'Primitive'}); break; } @@ -256,7 +267,7 @@ function* generateInstructionTypes( * We should change Hook to a subtype of Function or change unifier logic. * (see https://github.com/facebook/react-forget/pull/1427) */ - yield equation(value.callee.identifier.type, { + yield equation(getType(value.callee), { kind: 'Function', shapeId: null, return: returnType, @@ -272,7 +283,7 @@ function* generateInstructionTypes( * We should change Hook to a subtype of Function or change unifier logic. * (see https://github.com/facebook/react-forget/pull/1427) */ - yield equation(value.tag.identifier.type, { + yield equation(getType(value.tag), { kind: 'Function', shapeId: null, return: returnType, @@ -287,7 +298,7 @@ function* generateInstructionTypes( property.kind === 'ObjectProperty' && property.key.kind === 'computed' ) { - yield equation(property.key.name.identifier.type, { + yield equation(getType(property.key.name), { kind: 'Primitive', }); } @@ -304,7 +315,7 @@ function* generateInstructionTypes( case 'PropertyLoad': { yield equation(left, { kind: 'Property', - objectType: value.object.identifier.type, + objectType: getType(value.object), objectName: getName(names, value.object.identifier.id), propertyName: value.property, }); @@ -313,7 +324,7 @@ function* generateInstructionTypes( case 'MethodCall': { const returnType = makeType(); - yield equation(value.property.identifier.type, { + yield equation(getType(value.property), { kind: 'Function', return: returnType, shapeId: null, @@ -331,9 +342,9 @@ function* generateInstructionTypes( if (item.kind === 'Identifier') { // To simulate tuples we use properties with `String()`, eg "0". const propertyName = String(i); - yield equation(item.identifier.type, { + yield equation(getType(item), { kind: 'Property', - objectType: value.value.identifier.type, + objectType: getType(value.value), objectName: getName(names, value.value.identifier.id), propertyName, }); @@ -348,9 +359,9 @@ function* generateInstructionTypes( property.key.kind === 'identifier' || property.key.kind === 'string' ) { - yield equation(property.place.identifier.type, { + yield equation(getType(property.place), { kind: 'Property', - objectType: value.value.identifier.type, + objectType: getType(value.value), objectName: getName(names, value.value.identifier.id), propertyName: property.key.name, }); @@ -363,10 +374,10 @@ function* generateInstructionTypes( case 'TypeCastExpression': { if (env.config.enableUseTypeAnnotations) { - yield equation(value.type, value.value.identifier.type); + yield equation(value.type, getType(value.value)); yield equation(left, value.type); } else { - yield equation(left, value.value.identifier.type); + yield equation(left, getType(value.value)); } break; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts index 53640da502..6f0f55e685 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts @@ -18,7 +18,7 @@ import { IdentifierId, Place, SourceLocation, - getHookKind, + getHookKindForType, } from '../HIR/HIR'; import { eachInstructionLValue, @@ -224,7 +224,7 @@ export function validateHooksUsage(fn: HIRFunction): void { * directly a hook, or infer a Global kind from which knownhooks * can be derived later via property access (PropertyLoad etc) */ - if (getHookKind(fn.env, instr.lvalue.identifier) != null) { + if (getHookKindForType(fn.env, instr.lvalue.type) != null) { setKind(instr.lvalue, Kind.KnownHook); } else { setKind(instr.lvalue, Kind.Global); @@ -441,7 +441,7 @@ function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void { instr.value.kind === 'CallExpression' ? instr.value.callee : instr.value.property; - const hookKind = getHookKind(fn.env, callee.identifier); + const hookKind = getHookKindForType(fn.env, callee.type); if (hookKind != null) { errors.pushErrorDetail( new CompilerErrorDetail({ diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts index 9c41ebcae1..559fc74361 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts @@ -148,7 +148,7 @@ function getContextReassignment( if (value.kind === 'CallExpression') { const signature = getFunctionCallSignature( fn.env, - value.callee.identifier.type, + value.callee.type, ); if (signature?.noAlias) { operands = [value.callee]; @@ -156,16 +156,13 @@ function getContextReassignment( } else if (value.kind === 'MethodCall') { const signature = getFunctionCallSignature( fn.env, - value.property.identifier.type, + value.property.type, ); if (signature?.noAlias) { operands = [value.receiver, value.property]; } } else if (value.kind === 'TaggedTemplateExpression') { - const signature = getFunctionCallSignature( - fn.env, - value.tag.identifier.type, - ); + const signature = getFunctionCallSignature(fn.env, value.tag.type); if (signature?.noAlias) { operands = [value.tag]; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts index 364e78ae6b..a832f06e8f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts @@ -13,9 +13,7 @@ import { ReactiveInstruction, ReactiveScopeBlock, ScopeId, - isUseEffectHookType, - isUseInsertionEffectHookType, - isUseLayoutEffectHookType, + isEffectHook, } from '../HIR'; import {isMutable} from '../ReactiveScopes/InferReactiveScopeVariables'; import { @@ -93,7 +91,7 @@ class Visitor extends ReactiveFunctionVisitor { this.traverseInstruction(instruction, state); if ( instruction.value.kind === 'CallExpression' && - isEffectHook(instruction.value.callee.identifier) && + isEffectHook(instruction.value.callee.type) && instruction.value.args.length >= 2 ) { const deps = instruction.value.args[1]!; @@ -122,11 +120,3 @@ class Visitor extends ReactiveFunctionVisitor { function isUnmemoized(operand: Identifier, scopes: Set): boolean { return operand.scope != null && !scopes.has(operand.scope.id); } - -export function isEffectHook(identifier: Identifier): boolean { - return ( - isUseEffectHookType(identifier) || - isUseLayoutEffectHookType(identifier) || - isUseInsertionEffectHookType(identifier) - ); -} diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts index b361b2016a..7ba778154a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts @@ -105,9 +105,9 @@ export function validateNoRefAccessInRender(fn: HIRFunction): void { } function refTypeOfType(place: Place): RefAccessType { - if (isRefValueType(place.identifier)) { + if (isRefValueType(place.type)) { return {kind: 'RefValue'}; - } else if (isUseRefType(place.identifier)) { + } else if (isUseRefType(place.type)) { return {kind: 'Ref', refId: nextRefId()}; } else { return {kind: 'None'}; @@ -372,7 +372,7 @@ function validateNoRefAccessInRenderImpl( instr.value.kind === 'CallExpression' ? instr.value.callee : instr.value.property; - const hookKind = getHookKindForType(fn.env, callee.identifier.type); + const hookKind = getHookKindForType(fn.env, callee.type); let returnType: RefAccessType = {kind: 'None'}; const fnType = env.get(callee.identifier.id); if (fnType?.kind === 'Structure' && fnType.fn !== null) { @@ -498,7 +498,7 @@ function validateNoRefAccessInRenderImpl( } if ( - isUseRefType(instr.lvalue.identifier) && + isUseRefType(instr.lvalue.type) && env.get(instr.lvalue.identifier.id)?.kind !== 'Ref' ) { env.set( @@ -510,7 +510,7 @@ function validateNoRefAccessInRenderImpl( ); } if ( - isRefValueType(instr.lvalue.identifier) && + isRefValueType(instr.lvalue.type) && env.get(instr.lvalue.identifier.id)?.kind !== 'RefValue' ) { env.set( diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts index 2c6e7d8ac6..88253e05f1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts @@ -56,7 +56,7 @@ export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void { // faster-path to check if the function expression references a setState [...eachInstructionValueOperand(instr.value)].some( operand => - isSetStateType(operand.identifier) || + isSetStateType(operand.type) || setStateFunctions.has(operand.identifier.id), ) ) { @@ -76,7 +76,7 @@ export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void { instr.value.kind === 'MethodCall' ? instr.value.receiver : instr.value.callee; - if (isUseEffectHookType(callee.identifier)) { + if (isUseEffectHookType(callee.type)) { const arg = instr.value.args[0]; if (arg !== undefined && arg.kind === 'Identifier') { const setState = setStateFunctions.get(arg.identifier.id); @@ -135,7 +135,7 @@ function getSetStateCall( case 'CallExpression': { const callee = instr.value.callee; if ( - isSetStateType(callee.identifier) || + isSetStateType(callee.type) || setStateFunctions.has(callee.identifier.id) ) { /* diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts index 3f378b1289..1c51da3ba6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts @@ -79,7 +79,7 @@ function validateNoSetStateInRenderImpl( // faster-path to check if the function expression references a setState [...eachInstructionValueOperand(instr.value)].some( operand => - isSetStateType(operand.identifier) || + isSetStateType(operand.type) || unconditionalSetStateFunctions.has(operand.identifier.id), ) && // if yes, does it unconditonally call it? @@ -116,7 +116,7 @@ function validateNoSetStateInRenderImpl( case 'CallExpression': { const callee = instr.value.callee; if ( - isSetStateType(callee.identifier) || + isSetStateType(callee.type) || unconditionalSetStateFunctions.has(callee.identifier.id) ) { if (activeManualMemoId !== null) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts index e7615320c7..65eca99a47 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts @@ -251,6 +251,7 @@ function validateInferredDep( loc: GeneratedSource, effect: Effect.Read, reactive: false, + type: dep.type, }, }, path: [...dep.path], diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference.js new file mode 100644 index 0000000000..17ff229431 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference.js @@ -0,0 +1,12 @@ +function Component(props) { + useHook(); + const value = makeValue(props.value); + let result; + if (props.cond) { + console.log(value + 1); + result = value; + } else { + result = value.self(); + } + return result; +}