1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 */78import {CompilerDiagnostic, CompilerError} from '..';9import {ErrorCategory} from '../CompilerError';10import {Environment} from '../HIR/Environment';11import {HIRFunction, IdentifierId, Place} from '../HIR';12import {printPlace} from '../HIR/PrintHIR';13import {eachInstructionValueLValue, eachPatternOperand} from '../HIR/visitors';1415/**16 * Validates that all store/load references to a given named identifier align with the17 * "kind" of that variable (normal variable or context variable). For example, a context18 * variable may not be loaded/stored with regular StoreLocal/LoadLocal/Destructure instructions.19 */20export function validateContextVariableLValues(fn: HIRFunction): void {21 const identifierKinds: IdentifierKinds = new Map();22 validateContextVariableLValuesImpl(fn, identifierKinds, fn.env);23}2425function validateContextVariableLValuesImpl(26 fn: HIRFunction,27 identifierKinds: IdentifierKinds,28 env: Environment,29): void {30 for (const [, block] of fn.body.blocks) {31 for (const instr of block.instructions) {32 const {value} = instr;33 switch (value.kind) {34 case 'DeclareContext':35 case 'StoreContext': {36 visit(identifierKinds, value.lvalue.place, 'context', env);37 break;38 }39 case 'LoadContext': {40 visit(identifierKinds, value.place, 'context', env);41 break;42 }43 case 'StoreLocal':44 case 'DeclareLocal': {45 visit(identifierKinds, value.lvalue.place, 'local', env);46 break;47 }48 case 'LoadLocal': {49 visit(identifierKinds, value.place, 'local', env);50 break;51 }52 case 'PostfixUpdate':53 case 'PrefixUpdate': {54 visit(identifierKinds, value.lvalue, 'local', env);55 break;56 }57 case 'Destructure': {58 for (const lvalue of eachPatternOperand(value.lvalue.pattern)) {59 visit(identifierKinds, lvalue, 'destructure', env);60 }61 break;62 }63 case 'ObjectMethod':64 case 'FunctionExpression': {65 validateContextVariableLValuesImpl(66 value.loweredFunc.func,67 identifierKinds,68 env,69 );70 break;71 }72 default: {73 for (const _ of eachInstructionValueLValue(value)) {74 fn.env.recordError(75 CompilerDiagnostic.create({76 category: ErrorCategory.Todo,77 reason:78 'ValidateContextVariableLValues: unhandled instruction variant',79 description: `Handle '${value.kind} lvalues`,80 }).withDetails({81 kind: 'error',82 loc: value.loc,83 message: null,84 }),85 );86 }87 }88 }89 }90 }91}9293type IdentifierKinds = Map<94 IdentifierId,95 {place: Place; kind: 'local' | 'context' | 'destructure'}96>;9798function visit(99 identifiers: IdentifierKinds,100 place: Place,101 kind: 'local' | 'context' | 'destructure',102 env: Environment,103): void {104 const prev = identifiers.get(place.identifier.id);105 if (prev !== undefined) {106 const wasContext = prev.kind === 'context';107 const isContext = kind === 'context';108 if (wasContext !== isContext) {109 if (prev.kind === 'destructure' || kind === 'destructure') {110 env.recordError(111 CompilerDiagnostic.create({112 category: ErrorCategory.Todo,113 reason: `Support destructuring of context variables`,114 description: null,115 }).withDetails({116 kind: 'error',117 loc: kind === 'destructure' ? place.loc : prev.place.loc,118 message: null,119 }),120 );121 return;122 }123124 CompilerError.invariant(false, {125 reason:126 'Expected all references to a variable to be consistently local or context references',127 description: `Identifier ${printPlace(place)} is referenced as a ${kind} variable, but was previously referenced as a ${prev.kind} variable`,128 message: `this is ${prev.kind}`,129 loc: place.loc,130 });131 }132 }133 identifiers.set(place.identifier.id, {place, kind});134}
Findings
✓ No findings reported for this file.