From 0322e4dbd41769f335cf77d4fda0cf102c684a8b Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Thu, 18 May 2023 15:52:11 -0700 Subject: [PATCH] InferReferenceEffects uses function types for CallExpression Updates the InferReferenceEffects logic for CallExpression to work similarly to MethodCall, where we take into account the function signature (if present) when inferring the effects and return kind. --- compiler/forget/src/HIR/HIR.ts | 14 ++++--- .../src/Inference/InferReferenceEffects.ts | 41 +++++++++++++++++-- .../compiler/globals-Boolean.expect.md | 26 +++++++----- .../compiler/globals-Number.expect.md | 26 +++++++----- .../compiler/globals-String.expect.md | 26 +++++++----- 5 files changed, 90 insertions(+), 43 deletions(-) diff --git a/compiler/forget/src/HIR/HIR.ts b/compiler/forget/src/HIR/HIR.ts index 89538ca2a3..59a3988932 100644 --- a/compiler/forget/src/HIR/HIR.ts +++ b/compiler/forget/src/HIR/HIR.ts @@ -538,6 +538,13 @@ export type MethodCall = { loc: SourceLocation; }; +export type CallExpression = { + kind: "CallExpression"; + callee: Place; + args: Array; + loc: SourceLocation; +}; + /** * The value of a given instruction. Note that values are not recursive: complex * values such as objects or arrays are always defined by instructions to define @@ -605,12 +612,7 @@ export type InstructionValue = args: Array; loc: SourceLocation; } - | { - kind: "CallExpression"; - callee: Place; - args: Array; - loc: SourceLocation; - } + | CallExpression | MethodCall | { kind: "UnaryExpression"; diff --git a/compiler/forget/src/Inference/InferReferenceEffects.ts b/compiler/forget/src/Inference/InferReferenceEffects.ts index 0a9fbf7ae2..9ed2eca8c2 100644 --- a/compiler/forget/src/Inference/InferReferenceEffects.ts +++ b/compiler/forget/src/Inference/InferReferenceEffects.ts @@ -11,6 +11,7 @@ import { Environment } from "../HIR"; import { BasicBlock, BlockId, + CallExpression, Effect, HIRFunction, IdentifierId, @@ -707,8 +708,6 @@ function inferBlock( continue; } case "CallExpression": { - valueKind = ValueKind.Mutable; - effectKind = Effect.Mutate; const hook = instrValue.callee.identifier.type.kind === "Hook" ? instrValue.callee.identifier.type.definition @@ -716,8 +715,42 @@ function inferBlock( if (hook !== null) { effectKind = hook.effectKind; valueKind = hook.valueKind; + break; } - break; + + const signature = getFunctionCallSignature( + env, + instrValue.callee.identifier.type + ); + + const effects = + signature !== null + ? getMethodCallEffects(instrValue, signature) + : null; + for (let i = 0; i < instrValue.args.length; i++) { + const arg = instrValue.args[i]; + const place = arg.kind === "Identifier" ? arg : arg.place; + if (effects !== null) { + // If effects are inferred for an argument, we should fail invalid + // mutating effects + state.referenceAndCheckError(place, effects[i]); + } else { + state.reference(place, Effect.Mutate); + } + } + if (signature !== null) { + state.referenceAndCheckError( + instrValue.callee, + signature.calleeEffect + ); + } else { + state.reference(instrValue.callee, Effect.Mutate); + } + + state.initialize(instrValue, ValueKind.Mutable); + state.define(instr.lvalue, instrValue); + instr.lvalue.effect = Effect.Mutate; + continue; } case "MethodCall": { invariant( @@ -1003,7 +1036,7 @@ function getFunctionCallSignature( * @returns Inferred effects of function arguments, or null if inference fails. */ function getMethodCallEffects( - fn: MethodCall, + fn: MethodCall | CallExpression, sig: FunctionSignature ): Array | null { const results = []; diff --git a/compiler/forget/src/__tests__/fixtures/compiler/globals-Boolean.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/globals-Boolean.expect.md index 642b273630..84b9eb6b94 100644 --- a/compiler/forget/src/__tests__/fixtures/compiler/globals-Boolean.expect.md +++ b/compiler/forget/src/__tests__/fixtures/compiler/globals-Boolean.expect.md @@ -17,25 +17,29 @@ import { unstable_useMemoCache as useMemoCache } from "react"; function Component(props) { const $ = useMemoCache(3); let t0; - let x; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - x = {}; - t0 = Boolean(x); + t0 = {}; $[0] = t0; - $[1] = x; } else { t0 = $[0]; - x = $[1]; } - const y = t0; + const x = t0; let t1; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t1 = [x, y]; - $[2] = t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Boolean(x); + $[1] = t1; } else { - t1 = $[2]; + t1 = $[1]; } - return t1; + const y = t1; + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = [x, y]; + $[2] = t2; + } else { + t2 = $[2]; + } + return t2; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/compiler/globals-Number.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/globals-Number.expect.md index a8ce51aae2..7a29e07de6 100644 --- a/compiler/forget/src/__tests__/fixtures/compiler/globals-Number.expect.md +++ b/compiler/forget/src/__tests__/fixtures/compiler/globals-Number.expect.md @@ -17,25 +17,29 @@ import { unstable_useMemoCache as useMemoCache } from "react"; function Component(props) { const $ = useMemoCache(3); let t0; - let x; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - x = {}; - t0 = Number(x); + t0 = {}; $[0] = t0; - $[1] = x; } else { t0 = $[0]; - x = $[1]; } - const y = t0; + const x = t0; let t1; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t1 = [x, y]; - $[2] = t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Number(x); + $[1] = t1; } else { - t1 = $[2]; + t1 = $[1]; } - return t1; + const y = t1; + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = [x, y]; + $[2] = t2; + } else { + t2 = $[2]; + } + return t2; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/compiler/globals-String.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/globals-String.expect.md index 8955b4b326..1efd363393 100644 --- a/compiler/forget/src/__tests__/fixtures/compiler/globals-String.expect.md +++ b/compiler/forget/src/__tests__/fixtures/compiler/globals-String.expect.md @@ -17,25 +17,29 @@ import { unstable_useMemoCache as useMemoCache } from "react"; function Component(props) { const $ = useMemoCache(3); let t0; - let x; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - x = {}; - t0 = String(x); + t0 = {}; $[0] = t0; - $[1] = x; } else { t0 = $[0]; - x = $[1]; } - const y = t0; + const x = t0; let t1; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t1 = [x, y]; - $[2] = t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = String(x); + $[1] = t1; } else { - t1 = $[2]; + t1 = $[1]; } - return t1; + const y = t1; + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = [x, y]; + $[2] = t2; + } else { + t2 = $[2]; + } + return t2; } ```