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 5874fcfb06..0822a32657 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -23,6 +23,7 @@ import { Phi, Place, SpreadPattern, + TInstruction, Type, ValueKind, ValueReason, @@ -1238,62 +1239,12 @@ function inferBlock( break; } case 'CallExpression': { - const signature = getFunctionCallSignature( - env, - instrValue.callee.identifier.type, + inferCallEffects( + state, + instr as TInstruction, + freezeActions, + getFunctionCallSignature(env, instrValue.callee.identifier.type), ); - - const effects = - signature !== null ? getFunctionEffects(instrValue, signature) : null; - const returnValueKind: AbstractValue = - signature !== null - ? { - kind: signature.returnValueKind, - reason: new Set([ - signature.returnValueReason ?? - ValueReason.KnownReturnSignature, - ]), - context: new Set(), - } - : { - kind: ValueKind.Mutable, - reason: new Set([ValueReason.Other]), - context: new Set(), - }; - let hasCaptureArgument = false; - for (let i = 0; i < instrValue.args.length; i++) { - const arg = instrValue.args[i]; - const place = arg.kind === 'Identifier' ? arg : arg.place; - state.referenceAndRecordEffects( - freezeActions, - place, - getArgumentEffect(effects != null ? effects[i] : null, arg), - ValueReason.Other, - ); - hasCaptureArgument ||= place.effect === Effect.Capture; - } - if (signature !== null) { - state.referenceAndRecordEffects( - freezeActions, - instrValue.callee, - signature.calleeEffect, - ValueReason.Other, - ); - } else { - state.referenceAndRecordEffects( - freezeActions, - instrValue.callee, - Effect.ConditionallyMutate, - ValueReason.Other, - ); - } - hasCaptureArgument ||= instrValue.callee.effect === Effect.Capture; - - state.initialize(instrValue, returnValueKind); - state.define(instr.lvalue, instrValue); - instr.lvalue.effect = hasCaptureArgument - ? Effect.Store - : Effect.ConditionallyMutate; continuation = {kind: 'funeffects'}; break; } @@ -1311,102 +1262,12 @@ function inferBlock( Effect.Read, ValueReason.Other, ); - - const signature = getFunctionCallSignature( - env, - instrValue.property.identifier.type, + inferCallEffects( + state, + instr as TInstruction, + freezeActions, + getFunctionCallSignature(env, instrValue.property.identifier.type), ); - - const returnValueKind: AbstractValue = - signature !== null - ? { - kind: signature.returnValueKind, - reason: new Set([ - signature.returnValueReason ?? - ValueReason.KnownReturnSignature, - ]), - context: new Set(), - } - : { - kind: ValueKind.Mutable, - reason: new Set([ValueReason.Other]), - context: new Set(), - }; - - if ( - signature !== null && - signature.mutableOnlyIfOperandsAreMutable && - areArgumentsImmutableAndNonMutating(state, instrValue.args) - ) { - /* - * None of the args are mutable or mutate their params, we can downgrade to - * treating as all reads (except that the receiver may be captured) - */ - for (const arg of instrValue.args) { - const place = arg.kind === 'Identifier' ? arg : arg.place; - state.referenceAndRecordEffects( - freezeActions, - place, - Effect.Read, - ValueReason.Other, - ); - } - state.referenceAndRecordEffects( - freezeActions, - instrValue.receiver, - Effect.Capture, - ValueReason.Other, - ); - state.initialize(instrValue, returnValueKind); - state.define(instr.lvalue, instrValue); - instr.lvalue.effect = - instrValue.receiver.effect === Effect.Capture - ? Effect.Store - : Effect.ConditionallyMutate; - continuation = {kind: 'funeffects'}; - break; - } - - const effects = - signature !== null ? getFunctionEffects(instrValue, signature) : null; - let hasCaptureArgument = false; - for (let i = 0; i < instrValue.args.length; i++) { - const arg = instrValue.args[i]; - const place = arg.kind === 'Identifier' ? arg : arg.place; - /* - * If effects are inferred for an argument, we should fail invalid - * mutating effects - */ - state.referenceAndRecordEffects( - freezeActions, - place, - getArgumentEffect(effects != null ? effects[i] : null, arg), - ValueReason.Other, - ); - hasCaptureArgument ||= place.effect === Effect.Capture; - } - if (signature !== null) { - state.referenceAndRecordEffects( - freezeActions, - instrValue.receiver, - signature.calleeEffect, - ValueReason.Other, - ); - } else { - state.referenceAndRecordEffects( - freezeActions, - instrValue.receiver, - Effect.ConditionallyMutate, - ValueReason.Other, - ); - } - hasCaptureArgument ||= instrValue.receiver.effect === Effect.Capture; - - state.initialize(instrValue, returnValueKind); - state.define(instr.lvalue, instrValue); - instr.lvalue.effect = hasCaptureArgument - ? Effect.Store - : Effect.ConditionallyMutate; continuation = {kind: 'funeffects'}; break; } @@ -2125,3 +1986,105 @@ function getArgumentEffect( return Effect.ConditionallyMutate; } } + +function inferCallEffects( + state: InferenceState, + instr: TInstruction | TInstruction, + freezeActions: Array, + signature: FunctionSignature | null, +): void { + const instrValue = instr.value; + const returnValueKind: AbstractValue = + signature !== null + ? { + kind: signature.returnValueKind, + reason: new Set([ + signature.returnValueReason ?? ValueReason.KnownReturnSignature, + ]), + context: new Set(), + } + : { + kind: ValueKind.Mutable, + reason: new Set([ValueReason.Other]), + context: new Set(), + }; + + if ( + instrValue.kind === 'MethodCall' && + signature !== null && + signature.mutableOnlyIfOperandsAreMutable && + areArgumentsImmutableAndNonMutating(state, instrValue.args) + ) { + /* + * None of the args are mutable or mutate their params, we can downgrade to + * treating as all reads (except that the receiver may be captured) + */ + for (const arg of instrValue.args) { + const place = arg.kind === 'Identifier' ? arg : arg.place; + state.referenceAndRecordEffects( + freezeActions, + place, + Effect.Read, + ValueReason.Other, + ); + } + state.referenceAndRecordEffects( + freezeActions, + instrValue.receiver, + Effect.Capture, + ValueReason.Other, + ); + state.initialize(instrValue, returnValueKind); + state.define(instr.lvalue, instrValue); + instr.lvalue.effect = + instrValue.receiver.effect === Effect.Capture + ? Effect.Store + : Effect.ConditionallyMutate; + return; + } + + const effects = + signature !== null ? getFunctionEffects(instrValue, signature) : null; + let hasCaptureArgument = false; + for (let i = 0; i < instrValue.args.length; i++) { + const arg = instrValue.args[i]; + const place = arg.kind === 'Identifier' ? arg : arg.place; + /* + * If effects are inferred for an argument, we should fail invalid + * mutating effects + */ + state.referenceAndRecordEffects( + freezeActions, + place, + getArgumentEffect(effects != null ? effects[i] : null, arg), + ValueReason.Other, + ); + hasCaptureArgument ||= place.effect === Effect.Capture; + } + const callee = + instrValue.kind === 'CallExpression' + ? instrValue.callee + : instrValue.receiver; + if (signature !== null) { + state.referenceAndRecordEffects( + freezeActions, + callee, + signature.calleeEffect, + ValueReason.Other, + ); + } else { + state.referenceAndRecordEffects( + freezeActions, + callee, + Effect.ConditionallyMutate, + ValueReason.Other, + ); + } + hasCaptureArgument ||= callee.effect === Effect.Capture; + + state.initialize(instrValue, returnValueKind); + state.define(instr.lvalue, instrValue); + instr.lvalue.effect = hasCaptureArgument + ? Effect.Store + : Effect.ConditionallyMutate; +}