@samitouri / QOS-React / commits / 45463ab3ac

[compiler][be] Refactor similar CallExpression and MethodCall effect handling (#32696)

Simplify InferReferenceEffect function signature matching logic for next PRs in stack --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32696). * #32698 * #32697 * __->__ #32696 * #32695

mofeiZ committed Mar 23, 2025 at 23:07 UTC 45463ab3ac3ed0e65dfdbbfd5e53a50a8384e909
1 file changed +113 -150
compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts
+113 -150
@@ -23,6 +23,7 @@ import {
23 Phi,
24 Place,
25 SpreadPattern,
26 + TInstruction,
27 Type,
28 ValueKind,
29 ValueReason,
@@ -1238,62 +1239,12 @@ function inferBlock(
1239 break;
1240 }
1241 case 'CallExpression': {
1241 - const signature = getFunctionCallSignature(
1242 - env,
1243 - instrValue.callee.identifier.type,
1242 + inferCallEffects(
1243 + state,
1244 + instr as TInstruction<CallExpression>,
1245 + freezeActions,
1246 + getFunctionCallSignature(env, instrValue.callee.identifier.type),
1247 );
1245 -
1246 - const effects =
1247 - signature !== null ? getFunctionEffects(instrValue, signature) : null;
1248 - const returnValueKind: AbstractValue =
1249 - signature !== null
1250 - ? {
1251 - kind: signature.returnValueKind,
1252 - reason: new Set([
1253 - signature.returnValueReason ??
1254 - ValueReason.KnownReturnSignature,
1255 - ]),
1256 - context: new Set(),
1257 - }
1258 - : {
1259 - kind: ValueKind.Mutable,
1260 - reason: new Set([ValueReason.Other]),
1261 - context: new Set(),
1262 - };
1263 - let hasCaptureArgument = false;
1264 - for (let i = 0; i < instrValue.args.length; i++) {
1265 - const arg = instrValue.args[i];
1266 - const place = arg.kind === 'Identifier' ? arg : arg.place;
1267 - state.referenceAndRecordEffects(
1268 - freezeActions,
1269 - place,
1270 - getArgumentEffect(effects != null ? effects[i] : null, arg),
1271 - ValueReason.Other,
1272 - );
1273 - hasCaptureArgument ||= place.effect === Effect.Capture;
1274 - }
1275 - if (signature !== null) {
1276 - state.referenceAndRecordEffects(
1277 - freezeActions,
1278 - instrValue.callee,
1279 - signature.calleeEffect,
1280 - ValueReason.Other,
1281 - );
1282 - } else {
1283 - state.referenceAndRecordEffects(
1284 - freezeActions,
1285 - instrValue.callee,
1286 - Effect.ConditionallyMutate,
1287 - ValueReason.Other,
1288 - );
1289 - }
1290 - hasCaptureArgument ||= instrValue.callee.effect === Effect.Capture;
1291 -
1292 - state.initialize(instrValue, returnValueKind);
1293 - state.define(instr.lvalue, instrValue);
1294 - instr.lvalue.effect = hasCaptureArgument
1295 - ? Effect.Store
1296 - : Effect.ConditionallyMutate;
1248 continuation = {kind: 'funeffects'};
1249 break;
1250 }
@@ -1311,102 +1262,12 @@ function inferBlock(
1262 Effect.Read,
1263 ValueReason.Other,
1264 );
1314 -
1315 - const signature = getFunctionCallSignature(
1316 - env,
1317 - instrValue.property.identifier.type,
1265 + inferCallEffects(
1266 + state,
1267 + instr as TInstruction<MethodCall>,
1268 + freezeActions,
1269 + getFunctionCallSignature(env, instrValue.property.identifier.type),
1270 );
1319 -
1320 - const returnValueKind: AbstractValue =
1321 - signature !== null
1322 - ? {
1323 - kind: signature.returnValueKind,
1324 - reason: new Set([
1325 - signature.returnValueReason ??
1326 - ValueReason.KnownReturnSignature,
1327 - ]),
1328 - context: new Set(),
1329 - }
1330 - : {
1331 - kind: ValueKind.Mutable,
1332 - reason: new Set([ValueReason.Other]),
1333 - context: new Set(),
1334 - };
1335 -
1336 - if (
1337 - signature !== null &&
1338 - signature.mutableOnlyIfOperandsAreMutable &&
1339 - areArgumentsImmutableAndNonMutating(state, instrValue.args)
1340 - ) {
1341 - /*
1342 - * None of the args are mutable or mutate their params, we can downgrade to
1343 - * treating as all reads (except that the receiver may be captured)
1344 - */
1345 - for (const arg of instrValue.args) {
1346 - const place = arg.kind === 'Identifier' ? arg : arg.place;
1347 - state.referenceAndRecordEffects(
1348 - freezeActions,
1349 - place,
1350 - Effect.Read,
1351 - ValueReason.Other,
1352 - );
1353 - }
1354 - state.referenceAndRecordEffects(
1355 - freezeActions,
1356 - instrValue.receiver,
1357 - Effect.Capture,
1358 - ValueReason.Other,
1359 - );
1360 - state.initialize(instrValue, returnValueKind);
1361 - state.define(instr.lvalue, instrValue);
1362 - instr.lvalue.effect =
1363 - instrValue.receiver.effect === Effect.Capture
1364 - ? Effect.Store
1365 - : Effect.ConditionallyMutate;
1366 - continuation = {kind: 'funeffects'};
1367 - break;
1368 - }
1369 -
1370 - const effects =
1371 - signature !== null ? getFunctionEffects(instrValue, signature) : null;
1372 - let hasCaptureArgument = false;
1373 - for (let i = 0; i < instrValue.args.length; i++) {
1374 - const arg = instrValue.args[i];
1375 - const place = arg.kind === 'Identifier' ? arg : arg.place;
1376 - /*
1377 - * If effects are inferred for an argument, we should fail invalid
1378 - * mutating effects
1379 - */
1380 - state.referenceAndRecordEffects(
1381 - freezeActions,
1382 - place,
1383 - getArgumentEffect(effects != null ? effects[i] : null, arg),
1384 - ValueReason.Other,
1385 - );
1386 - hasCaptureArgument ||= place.effect === Effect.Capture;
1387 - }
1388 - if (signature !== null) {
1389 - state.referenceAndRecordEffects(
1390 - freezeActions,
1391 - instrValue.receiver,
1392 - signature.calleeEffect,
1393 - ValueReason.Other,
1394 - );
1395 - } else {
1396 - state.referenceAndRecordEffects(
1397 - freezeActions,
1398 - instrValue.receiver,
1399 - Effect.ConditionallyMutate,
1400 - ValueReason.Other,
1401 - );
1402 - }
1403 - hasCaptureArgument ||= instrValue.receiver.effect === Effect.Capture;
1404 -
1405 - state.initialize(instrValue, returnValueKind);
1406 - state.define(instr.lvalue, instrValue);
1407 - instr.lvalue.effect = hasCaptureArgument
1408 - ? Effect.Store
1409 - : Effect.ConditionallyMutate;
1271 continuation = {kind: 'funeffects'};
1272 break;
1273 }
@@ -2125,3 +1986,105 @@ function getArgumentEffect(
1986 return Effect.ConditionallyMutate;
1987 }
1988 }
1989 +
1990 +function inferCallEffects(
1991 + state: InferenceState,
1992 + instr: TInstruction<CallExpression> | TInstruction<MethodCall>,
1993 + freezeActions: Array<FreezeAction>,
1994 + signature: FunctionSignature | null,
1995 +): void {
1996 + const instrValue = instr.value;
1997 + const returnValueKind: AbstractValue =
1998 + signature !== null
1999 + ? {
2000 + kind: signature.returnValueKind,
2001 + reason: new Set([
2002 + signature.returnValueReason ?? ValueReason.KnownReturnSignature,
2003 + ]),
2004 + context: new Set(),
2005 + }
2006 + : {
2007 + kind: ValueKind.Mutable,
2008 + reason: new Set([ValueReason.Other]),
2009 + context: new Set(),
2010 + };
2011 +
2012 + if (
2013 + instrValue.kind === 'MethodCall' &&
2014 + signature !== null &&
2015 + signature.mutableOnlyIfOperandsAreMutable &&
2016 + areArgumentsImmutableAndNonMutating(state, instrValue.args)
2017 + ) {
2018 + /*
2019 + * None of the args are mutable or mutate their params, we can downgrade to
2020 + * treating as all reads (except that the receiver may be captured)
2021 + */
2022 + for (const arg of instrValue.args) {
2023 + const place = arg.kind === 'Identifier' ? arg : arg.place;
2024 + state.referenceAndRecordEffects(
2025 + freezeActions,
2026 + place,
2027 + Effect.Read,
2028 + ValueReason.Other,
2029 + );
2030 + }
2031 + state.referenceAndRecordEffects(
2032 + freezeActions,
2033 + instrValue.receiver,
2034 + Effect.Capture,
2035 + ValueReason.Other,
2036 + );
2037 + state.initialize(instrValue, returnValueKind);
2038 + state.define(instr.lvalue, instrValue);
2039 + instr.lvalue.effect =
2040 + instrValue.receiver.effect === Effect.Capture
2041 + ? Effect.Store
2042 + : Effect.ConditionallyMutate;
2043 + return;
2044 + }
2045 +
2046 + const effects =
2047 + signature !== null ? getFunctionEffects(instrValue, signature) : null;
2048 + let hasCaptureArgument = false;
2049 + for (let i = 0; i < instrValue.args.length; i++) {
2050 + const arg = instrValue.args[i];
2051 + const place = arg.kind === 'Identifier' ? arg : arg.place;
2052 + /*
2053 + * If effects are inferred for an argument, we should fail invalid
2054 + * mutating effects
2055 + */
2056 + state.referenceAndRecordEffects(
2057 + freezeActions,
2058 + place,
2059 + getArgumentEffect(effects != null ? effects[i] : null, arg),
2060 + ValueReason.Other,
2061 + );
2062 + hasCaptureArgument ||= place.effect === Effect.Capture;
2063 + }
2064 + const callee =
2065 + instrValue.kind === 'CallExpression'
2066 + ? instrValue.callee
2067 + : instrValue.receiver;
2068 + if (signature !== null) {
2069 + state.referenceAndRecordEffects(
2070 + freezeActions,
2071 + callee,
2072 + signature.calleeEffect,
2073 + ValueReason.Other,
2074 + );
2075 + } else {
2076 + state.referenceAndRecordEffects(
2077 + freezeActions,
2078 + callee,
2079 + Effect.ConditionallyMutate,
2080 + ValueReason.Other,
2081 + );
2082 + }
2083 + hasCaptureArgument ||= callee.effect === Effect.Capture;
2084 +
2085 + state.initialize(instrValue, returnValueKind);
2086 + state.define(instr.lvalue, instrValue);
2087 + instr.lvalue.effect = hasCaptureArgument
2088 + ? Effect.Store
2089 + : Effect.ConditionallyMutate;
2090 +}