/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import {CompilerError} from '../CompilerError';
import {GeneratedSource, HIRFunction} from './HIR';
import {printTerminal} from './PrintHIR';
import {eachTerminalSuccessor, mapTerminalSuccessors} from './visitors';

export function assertTerminalSuccessorsExist(fn: HIRFunction): void {
  for (const [, block] of fn.body.blocks) {
    mapTerminalSuccessors(block.terminal, successor => {
      CompilerError.invariant(fn.body.blocks.has(successor), {
        reason: `Terminal successor references unknown block`,
        description: `Block bb${successor} does not exist for terminal '${printTerminal(
          block.terminal,
        )}'`,
        loc: (block.terminal as any).loc ?? GeneratedSource,
        suggestions: null,
      });
      return successor;
    });
  }
}

export function assertTerminalPredsExist(fn: HIRFunction): void {
  for (const [, block] of fn.body.blocks) {
    for (const pred of block.preds) {
      const predBlock = fn.body.blocks.get(pred);
      CompilerError.invariant(predBlock != null, {
        reason: 'Expected predecessor block to exist',
        description: `Block ${block.id} references non-existent ${pred}`,
        loc: GeneratedSource,
      });
      CompilerError.invariant(
        [...eachTerminalSuccessor(predBlock.terminal)].includes(block.id),
        {
          reason: 'Terminal successor does not reference correct predecessor',
          description: `Block bb${block.id} has bb${predBlock.id} as a predecessor, but bb${predBlock.id}'s successors do not include bb${block.id}`,
          loc: GeneratedSource,
        },
      );
    }
  }
}
