Use CompilerError.invariant consistently

This PR updates all current usages of invariant to CompilerError.invariant along 
with any available locs
This commit is contained in:
Lauren Tan
2023-06-22 10:18:42 -04:00
parent e9faa5b90d
commit e7e67f335a
25 changed files with 313 additions and 202 deletions
@@ -8,7 +8,6 @@
import { NodePath, Scope } from "@babel/traverse";
import * as t from "@babel/types";
import { Expression } from "@babel/types";
import invariant from "invariant";
import { CompilerError, ErrorSeverity } from "../CompilerError";
import { Err, Ok, Result } from "../Utils/Result";
import { assertExhaustive } from "../Utils/utils";
@@ -769,9 +768,10 @@ function lowerStatement(
case "FunctionDeclaration": {
const stmt = stmtPath as NodePath<t.FunctionDeclaration>;
stmt.skip();
invariant(
CompilerError.invariant(
stmt.get("id").type === "Identifier",
"function declarations must have a name"
"function declarations must have a name",
stmt.node.loc ?? null
);
const id = stmt.get("id") as NodePath<t.Identifier>;
@@ -795,9 +795,10 @@ function lowerStatement(
),
])
);
invariant(
CompilerError.invariant(
desugared.length === 1,
"only one declaration is created from desugaring function declaration"
"only one declaration is created from desugaring function declaration",
stmt.node.loc ?? null
);
lowerStatement(builder, desugared.at(0)!);
return;
@@ -843,9 +844,10 @@ function lowerStatement(
let test: Place;
if (left.isVariableDeclaration()) {
const declarations = left.get("declarations");
invariant(
CompilerError.invariant(
declarations.length === 1,
`Expected only one declaration in the init of a ForOfStatement, got ${declarations.length}`
`Expected only one declaration in the init of a ForOfStatement, got ${declarations.length}`,
left.node.loc ?? null
);
const id = declarations[0].get("id");
const nextIterableOf = lowerValueToTemporary(builder, {
@@ -1517,7 +1519,11 @@ function lowerExpression(
});
}
} else {
invariant(namePath.isJSXNamespacedName(), "Refinement");
CompilerError.invariant(
namePath.isJSXNamespacedName(),
"Refinement",
namePath.node.loc ?? null
);
const namespace = namePath.node.namespace.name;
const name = namePath.node.name.name;
propName = `${namespace}:${name}`;
@@ -1586,9 +1592,10 @@ function lowerExpression(
});
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
}
invariant(
CompilerError.invariant(
expr.get("quasi").get("quasis").length == 1,
"there should be only one quasi as we don't support interpolations yet"
"there should be only one quasi as we don't support interpolations yet",
expr.node.loc ?? null
);
const value = expr.get("quasi").get("quasis").at(0)!.node.value;
if (value.raw !== value.cooked) {
@@ -1836,7 +1843,7 @@ function lowerOptionalMemberExpression(
loc,
};
});
invariant(object !== null, "Satisfy type checker");
CompilerError.invariant(object !== null, "Satisfy type checker", null);
// block to evaluate if the callee is non-null/undefined. arguments are lowered in this block to preserve
// the semantic of conditional evaluation depending on the callee
@@ -2268,10 +2275,10 @@ function lowerJsxMemberExpression(
if (object.isJSXMemberExpression()) {
objectPlace = lowerJsxMemberExpression(builder, object);
} else {
invariant(
CompilerError.invariant(
object.isJSXIdentifier(),
"TypeScript refinement fail: expected 'JsxIdentifier', got '%s'",
object.node.type
`TypeScript refinement fail: expected 'JsxIdentifier', got '${object.node.type}'`,
object.node.loc ?? null
);
objectPlace = lowerIdentifier(builder, object);
}
@@ -2303,9 +2310,10 @@ function lowerJsxElement(
if (expression.isJSXEmptyExpression()) {
return null;
} else {
invariant(
CompilerError.invariant(
expression.isExpression(),
`(BuildHIR::lowerJsxElement) Expected Expression but found ${expression.type}!`
`(BuildHIR::lowerJsxElement) Expected Expression but found ${expression.type}!`,
expression.node.loc ?? null
);
return lowerExpressionToTemporary(builder, expression);
}
@@ -2554,9 +2562,10 @@ function lowerAssignment(
}
case "MemberExpression": {
// This can only occur because of a coding error, parsers enforce this condition
invariant(
CompilerError.invariant(
kind === InstructionKind.Reassign,
"MemberExpression may only appear in an assignment expression"
"MemberExpression may only appear in an assignment expression",
lvaluePath.node.loc ?? null
);
const lvalue = lvaluePath as NodePath<t.MemberExpression>;
const property = lvalue.get("property");
@@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import prettyFormat from "pretty-format";
import { CompilerError } from "../CompilerError";
import { BlockId, HIRFunction } from "./HIR";
import { eachTerminalSuccessor } from "./visitors";
@@ -88,7 +88,7 @@ export class Dominator<T> {
*/
get(id: T): T | null {
const dominator = this.#nodes.get(id);
invariant(dominator !== undefined, "Unknown node");
CompilerError.invariant(dominator !== undefined, "Unknown node", null);
return dominator === id ? null : dominator;
}
@@ -119,7 +119,7 @@ export class PostDominator<T> {
*/
get(id: T): T | null {
const dominator = this.#nodes.get(id);
invariant(dominator !== undefined, "Unknown node");
CompilerError.invariant(dominator !== undefined, "Unknown node", null);
return dominator === id ? null : dominator;
}
@@ -159,9 +159,10 @@ function computeImmediateDominators<T>(graph: Graph<T>): Map<T, T> {
break;
}
}
invariant(
CompilerError.invariant(
newIdom !== null,
`At least one predecessor must have been visited for block ${id}`
`At least one predecessor must have been visited for block ${id}`,
null
);
for (const pred of node.preds) {
@@ -6,7 +6,7 @@
*/
import * as t from "@babel/types";
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import { ExternalFunction } from "../Entrypoint/Options";
import { log } from "../Utils/logger";
import {
@@ -195,9 +195,10 @@ export class Environment {
if (config?.customHooks) {
this.#globals = new Map(DEFAULT_GLOBALS);
for (const [hookName, hook] of config.customHooks) {
invariant(
CompilerError.invariant(
!this.#globals.has(hookName),
`[Globals] Found existing definition in global registry for custom hook ${hookName}`
`[Globals] Found existing definition in global registry for custom hook ${hookName}`,
null
);
this.#globals.set(
hookName,
@@ -270,9 +271,10 @@ export class Environment {
// If an object or function has a shapeId, it must have been assigned
// by Forget (and be present in a builtin or user-defined registry)
const shape = this.#shapes.get(shapeId);
invariant(
CompilerError.invariant(
shape !== undefined,
`[HIR] Forget internal error: cannot resolve shape ${shapeId}`
`[HIR] Forget internal error: cannot resolve shape ${shapeId}`,
null
);
return shape.properties.get(property) ?? null;
} else {
@@ -284,9 +286,10 @@ export class Environment {
const { shapeId } = type;
if (shapeId !== null) {
const shape = this.#shapes.get(shapeId);
invariant(
CompilerError.invariant(
shape !== undefined,
`[HIR] Forget internal error: cannot resolve shape ${shapeId}`
`[HIR] Forget internal error: cannot resolve shape ${shapeId}`,
null
);
return shape.functionType;
}
@@ -6,7 +6,6 @@
*/
import * as t from "@babel/types";
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import { assertExhaustive } from "../Utils/utils";
import { Environment } from "./Environment";
@@ -922,9 +921,10 @@ const opaqueBlockId = Symbol();
export type BlockId = number & { [opaqueBlockId]: "BlockId" };
export function makeBlockId(id: number): BlockId {
invariant(
CompilerError.invariant(
id >= 0 && Number.isInteger(id),
"Expected block id to be a non-negative integer"
"Expected block id to be a non-negative integer",
null
);
return id as BlockId;
}
@@ -937,9 +937,10 @@ const opaqueScopeId = Symbol();
export type ScopeId = number & { [opaqueScopeId]: "ScopeId" };
export function makeScopeId(id: number): ScopeId {
invariant(
CompilerError.invariant(
id >= 0 && Number.isInteger(id),
"Expected block id to be a non-negative integer"
"Expected block id to be a non-negative integer",
null
);
return id as ScopeId;
}
@@ -952,9 +953,10 @@ const opaqueIdentifierId = Symbol();
export type IdentifierId = number & { [opaqueIdentifierId]: "IdentifierId" };
export function makeIdentifierId(id: number): IdentifierId {
invariant(
CompilerError.invariant(
id >= 0 && Number.isInteger(id),
"Expected identifier id to be a non-negative integer"
"Expected identifier id to be a non-negative integer",
null
);
return id as IdentifierId;
}
@@ -967,9 +969,10 @@ const opaqueInstructionId = Symbol();
export type InstructionId = number & { [opaqueInstructionId]: "IdentifierId" };
export function makeInstructionId(id: number): InstructionId {
invariant(
CompilerError.invariant(
id >= 0 && Number.isInteger(id),
"Expected instruction id to be a non-negative integer"
"Expected instruction id to be a non-negative integer",
null
);
return id as InstructionId;
}
@@ -7,7 +7,6 @@
import { Binding, NodePath } from "@babel/traverse";
import * as t from "@babel/types";
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import { assertExhaustive } from "../Utils/utils";
import { Environment } from "./Environment";
@@ -388,12 +387,13 @@ export default class HIRBuilder {
});
const value = fn();
const last = this.#scopes.pop();
invariant(
CompilerError.invariant(
last != null &&
last.kind === "label" &&
last.label === label &&
last.breakBlock === breakBlock,
"Mismatched label"
"Mismatched label",
null
);
return value;
}
@@ -406,12 +406,13 @@ export default class HIRBuilder {
});
const value = fn();
const last = this.#scopes.pop();
invariant(
CompilerError.invariant(
last != null &&
last.kind === "switch" &&
last.label === label &&
last.breakBlock === breakBlock,
"Mismatched label"
"Mismatched label",
null
);
return value;
}
@@ -440,13 +441,14 @@ export default class HIRBuilder {
});
const value = fn();
const last = this.#scopes.pop();
invariant(
CompilerError.invariant(
last != null &&
last.kind === "loop" &&
last.label === label &&
last.continueBlock === continueBlock &&
last.breakBlock === breakBlock,
"Mismatched loops"
"Mismatched loops",
null
);
return value;
}
@@ -462,7 +464,11 @@ export default class HIRBuilder {
return scope.breakBlock;
}
}
invariant(false, "Expected a loop or switch to be in scope");
CompilerError.invariant(
false,
"Expected a loop or switch to be in scope",
null
);
}
/**
@@ -478,10 +484,14 @@ export default class HIRBuilder {
return scope.continueBlock;
}
} else if (label !== null && scope.label === label) {
invariant(false, "Continue may only refer to a labeled loop");
CompilerError.invariant(
false,
"Continue may only refer to a labeled loop",
null
);
}
}
invariant(false, "Expected a loop to be in scope");
CompilerError.invariant(false, "Expected a loop to be in scope", null);
}
}
@@ -501,7 +511,11 @@ function _shrink(func: HIR): void {
return target;
}
const block = func.blocks.get(blockId);
invariant(block != null, "expected block %s to exist", blockId);
CompilerError.invariant(
block != null,
`expected block ${blockId} to exist`,
null
);
target = getTargetIfIndirection(block);
if (target !== null) {
// the target might also be a simple goto, recurse
@@ -717,9 +731,10 @@ export function markInstructionIds(func: HIR): void {
const visited = new Set<Instruction>();
for (const [_, block] of func.blocks) {
for (const instr of block.instructions) {
invariant(
CompilerError.invariant(
!visited.has(instr),
`${printInstruction(instr)} already visited!`
`${printInstruction(instr)} already visited!`,
instr.loc
);
visited.add(instr);
instr.id = makeInstructionId(++id);
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import {
BlockId,
Effect,
@@ -47,10 +47,10 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
const originalPredecessorId = Array.from(block.preds)[0]!;
const predecessorId = merged.get(originalPredecessorId);
const predecessor = fn.body.blocks.get(predecessorId);
invariant(
CompilerError.invariant(
predecessor !== undefined,
"Expected predecessor %s to exist",
predecessorId
`Expected predecessor ${predecessorId} to exist`,
null
);
if (predecessor.terminal.kind !== "goto" || predecessor.kind !== "block") {
// The predecessor is not guaranteed to transfer control to this block,
@@ -60,10 +60,10 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
// Replace phis in the merged block with canonical assignments to the single operand value
for (const phi of block.phis) {
invariant(
CompilerError.invariant(
phi.operands.size === 1,
"Found a block with a single predecessor but where a phi has multiple (%s) operands",
phi.operands.size
`Found a block with a single predecessor but where a phi has multiple (${phi.operands.size}) operands`,
null
);
const operand = Array.from(phi.operands.values())[0]!;
const instr: Instruction = {
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import { Effect, ValueKind } from "./HIR";
import {
BuiltInType,
@@ -103,9 +103,10 @@ function addShape(
functionType,
};
invariant(
CompilerError.invariant(
!registry.has(id),
`[ObjectShape] Could not add shape to registry: name ${id} already exists.`
`[ObjectShape] Could not add shape to registry: name ${id} already exists.`,
null
);
registry.set(id, shape);
return shape;
@@ -6,7 +6,7 @@
*/
import generate from "@babel/generator";
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import DisjointSet from "../Utils/DisjointSet";
import { assertExhaustive } from "../Utils/utils";
import {
@@ -467,9 +467,10 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
}
case "TemplateLiteral": {
value = "`";
invariant(
CompilerError.invariant(
instrValue.subexprs.length === instrValue.quasis.length - 1,
"Bad assumption about quasi length."
"Bad assumption about quasi length.",
instrValue.loc
);
for (let i = 0; i < instrValue.subexprs.length; i++) {
value += instrValue.quasis[i].raw;
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
export type BuiltInType = PrimitiveType | FunctionType | ObjectType;
@@ -62,9 +62,10 @@ const opaqueTypeId = Symbol();
export type TypeId = number & { [opaqueTypeId]: "IdentifierId" };
export function makeTypeId(id: number): TypeId {
invariant(
CompilerError.invariant(
id >= 0 && Number.isInteger(id),
"Expected instruction id to be a non-negative integer"
"Expected instruction id to be a non-negative integer",
null
);
return id as TypeId;
}
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import {
Effect,
FunctionExpression,
@@ -169,9 +169,10 @@ function infer(
// In practice this never really matters because the Component function has no
// context refs, so it will never have duplicate deps.
for (const place of context) {
invariant(
CompilerError.invariant(
place.identifier.name !== null,
"context refs should always have a name"
"context refs should always have a name",
place.loc
);
const effect = mutations.get(place.identifier.name);
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import {
Effect,
HIRFunction,
@@ -108,9 +108,10 @@ export function inferMutableLifetimes(
start = Math.min(start, operand.mutableRange.start);
end = Math.max(end, operand.mutableRange.end);
}
invariant(
CompilerError.invariant(
start !== Number.MAX_SAFE_INTEGER,
"Expected phi to have a start range value"
"Expected phi to have a start range value",
null
);
phi.id.mutableRange = {
start: makeInstructionId(start),
@@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import { Environment } from "../HIR";
import {
@@ -192,9 +191,10 @@ class InferenceState {
* (Re)initializes a @param value with its default @param kind.
*/
initialize(value: InstructionValue, kind: ValueKind): void {
invariant(
CompilerError.invariant(
value.kind !== "LoadLocal",
"Expected all top-level identifiers to be defined as variables, not values"
"Expected all top-level identifiers to be defined as variables, not values",
value.loc
);
this.#values.set(value, kind);
}
@@ -204,11 +204,12 @@ class InferenceState {
*/
kind(place: Place): ValueKind {
const values = this.#variables.get(place.identifier.id);
invariant(
CompilerError.invariant(
values != null,
`Expected value kind to be initialized at '${printSourceLocation(
place.loc
)}'`
)}'`,
place.loc
);
let mergedKind: ValueKind | null = null;
for (const value of values) {
@@ -229,10 +230,10 @@ class InferenceState {
*/
alias(place: Place, value: Place): void {
const values = this.#variables.get(value.identifier.id);
invariant(
CompilerError.invariant(
values != null,
"Expected value for identifier `%s` to be initialized.",
value.identifier.id
`Expected value for identifier \`${value.identifier.id}\` to be initialized.`,
value.loc
);
this.#variables.set(place.identifier.id, new Set(values));
}
@@ -241,9 +242,10 @@ class InferenceState {
* Defines (initializing or updating) a variable with a specific kind of value.
*/
define(place: Place, value: InstructionValue): void {
invariant(
CompilerError.invariant(
this.#values.has(value),
`Expected value to be initialized at '${printSourceLocation(value.loc)}'`
`Expected value to be initialized at '${printSourceLocation(value.loc)}'`,
value.loc
);
this.#variables.set(place.identifier.id, new Set([value]));
}
@@ -363,9 +365,10 @@ class InferenceState {
break;
}
case Effect.Unknown: {
invariant(
CompilerError.invariant(
false,
"Unexpected unknown effect, expected to infer a precise effect kind"
"Unexpected unknown effect, expected to infer a precise effect kind",
place.loc
);
}
default: {
@@ -375,7 +378,11 @@ class InferenceState {
);
}
}
invariant(effect !== null, "Expected effect to be set");
CompilerError.invariant(
effect !== null,
"Expected effect to be set",
place.loc
);
place.effect = effect;
}
@@ -746,9 +753,10 @@ function inferBlock(
continue;
}
case "MethodCall": {
invariant(
CompilerError.invariant(
state.isDefined(instrValue.receiver),
"[InferReferenceEffects] Internal error: receiver of PropertyCall should have been defined by corresponding PropertyLoad"
"[InferReferenceEffects] Internal error: receiver of PropertyCall should have been defined by corresponding PropertyLoad",
instrValue.loc
);
state.reference(instrValue.property, Effect.Read);
@@ -882,9 +890,10 @@ function inferBlock(
const lvalue = instr.lvalue;
lvalue.effect = Effect.ConditionallyMutate;
const valueKind = state.kind(instrValue.place);
invariant(
CompilerError.invariant(
valueKind === ValueKind.Mutable || valueKind === ValueKind.Context,
"[InferReferenceEffects] Context variables are always mutable."
"[InferReferenceEffects] Context variables are always mutable.",
instrValue.loc
);
state.initialize(instrValue, valueKind);
state.define(lvalue, instrValue);
@@ -971,10 +980,10 @@ function inferBlock(
}
for (const operand of eachInstructionOperand(instr)) {
invariant(
CompilerError.invariant(
effectKind != null,
"effectKind must be set for instruction value `%s`",
instrValue.kind
`effectKind must be set for instruction value \`${instrValue.kind}\``,
instrValue.loc
);
state.reference(operand, effectKind);
}
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import {
BlockId,
InstructionId,
@@ -70,7 +70,11 @@ class Context {
this.#builders.push(builder);
fn();
const popped = this.#builders.pop();
invariant(popped === builder, "Expected push/pop to be called 1:1");
CompilerError.invariant(
popped === builder,
"Expected push/pop to be called 1:1",
null
);
return builder.complete();
}
}
@@ -90,7 +94,11 @@ class Builder {
append(item: ReactiveStatement, label: BlockId | null): void {
if (label !== null) {
invariant(item.kind === "terminal", "Only terminals may have a label");
CompilerError.invariant(
item.kind === "terminal",
"Only terminals may have a label",
null
);
item.label = label;
}
this.#instructions.push(item);
@@ -130,9 +138,10 @@ class Builder {
// "Expected all scopes to be closed when exiting a block"
// );
const first = this.#stack[0]!;
invariant(
CompilerError.invariant(
first.kind === "block",
"Expected first stack item to be a basic block"
"Expected first stack item to be a basic block",
null
);
return first.block;
}
@@ -164,9 +173,10 @@ function visitBlock(context: Context, block: ReactiveBlock): void {
break;
}
case "scope": {
invariant(
CompilerError.invariant(
false,
"Expected the function to not have scopes already assigned"
"Expected the function to not have scopes already assigned",
null
);
}
default: {
@@ -182,10 +192,11 @@ function visitBlock(context: Context, block: ReactiveBlock): void {
export function getInstructionScope(
instr: ReactiveInstruction
): ReactiveScope | null {
invariant(
CompilerError.invariant(
instr.lvalue !== null,
"Expected lvalues to not be null when assigning scopes. " +
"Pruning lvalues too early can result in missing scope information."
"Pruning lvalues too early can result in missing scope information.",
instr.loc
);
for (const operand of eachInstructionLValue(instr)) {
const operandScope = getPlaceScope(instr.id, operand);
@@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import {
BasicBlock,
@@ -66,9 +65,10 @@ class Driver {
}
visitBlock(block: BasicBlock, blockValue: ReactiveBlock): void {
invariant(
CompilerError.invariant(
!this.cx.emitted.has(block.id),
`Cannot emit the same block twice: bb${block.id}`
`Cannot emit the same block twice: bb${block.id}`,
null
);
this.cx.emitted.add(block.id);
for (const instruction of block.instructions) {
@@ -252,9 +252,10 @@ class Driver {
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
} else {
const break_ = this.visitBreak(terminal.loop, null);
invariant(
CompilerError.invariant(
break_ !== null,
"If loop body is already scheduled it must be a break"
"If loop body is already scheduled it must be a break",
null
);
loopBody = [break_];
}
@@ -308,9 +309,10 @@ class Driver {
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
} else {
const break_ = this.visitBreak(terminal.loop, null);
invariant(
CompilerError.invariant(
break_ !== null,
"If loop body is already scheduled it must be a break"
"If loop body is already scheduled it must be a break",
null
);
loopBody = [break_];
}
@@ -392,9 +394,10 @@ class Driver {
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
} else {
const break_ = this.visitBreak(terminal.loop, null);
invariant(
CompilerError.invariant(
break_ !== null,
"If loop body is already scheduled it must be a break"
"If loop body is already scheduled it must be a break",
null
);
loopBody = [break_];
}
@@ -467,9 +470,10 @@ class Driver {
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
} else {
const break_ = this.visitBreak(terminal.loop, null);
invariant(
CompilerError.invariant(
break_ !== null,
"If loop body is already scheduled it must be a break"
"If loop body is already scheduled it must be a break",
null
);
loopBody = [break_];
}
@@ -626,7 +630,11 @@ class Driver {
break;
}
case "unsupported": {
invariant(false, "Unexpected unsupported terminal");
CompilerError.invariant(
false,
"Unexpected unsupported terminal",
terminal.loc
);
}
default: {
assertExhaustive(terminal, "Unexpected terminal");
@@ -654,10 +662,11 @@ class Driver {
};
} else if (defaultBlock.instructions.length === 1) {
const instr = defaultBlock.instructions[0]!;
invariant(
CompilerError.invariant(
instr.lvalue.identifier.id ===
defaultBlock.terminal.test.identifier.id,
"Expected branch block to end in an instruction that sets the test value"
"Expected branch block to end in an instruction that sets the test value",
instr.lvalue.loc
);
return {
block: defaultBlock.id,
@@ -684,9 +693,10 @@ class Driver {
} else if (defaultBlock.terminal.kind === "goto") {
const instructions = defaultBlock.instructions;
if (instructions.length === 0) {
invariant(
CompilerError.invariant(
false,
"Expected goto value block to have at least one instruction"
"Expected goto value block to have at least one instruction",
null
);
} else if (defaultBlock.instructions.length === 1) {
const instr = defaultBlock.instructions[0]!;
@@ -782,10 +792,10 @@ class Driver {
case "optional": {
const test = this.visitValueBlock(terminal.test, terminal.loc);
const testBlock = this.cx.ir.blocks.get(test.block)!;
invariant(
CompilerError.invariant(
testBlock.terminal.kind === "branch",
"Unexpected terminal kind '%s' for optional call test block",
testBlock.terminal.kind
`Unexpected terminal kind '${testBlock.terminal.kind}' for optional call test block`,
testBlock.terminal.loc
);
const consequent = this.visitValueBlock(
testBlock.terminal.consequent,
@@ -821,10 +831,10 @@ class Driver {
case "logical": {
const test = this.visitValueBlock(terminal.test, terminal.loc);
const testBlock = this.cx.ir.blocks.get(test.block)!;
invariant(
CompilerError.invariant(
testBlock.terminal.kind === "branch",
"Unexpected terminal kind '%s' for logical test block",
testBlock.terminal.kind
`Unexpected terminal kind '${testBlock.terminal.kind}' for logical test block`,
testBlock.terminal.loc
);
const leftFinal = this.visitValueBlock(
@@ -866,10 +876,10 @@ class Driver {
case "ternary": {
const test = this.visitValueBlock(terminal.test, terminal.loc);
const testBlock = this.cx.ir.blocks.get(test.block)!;
invariant(
CompilerError.invariant(
testBlock.terminal.kind === "branch",
"Unexpected terminal kind '%s' for ternary test block",
testBlock.terminal.kind
`Unexpected terminal kind '${testBlock.terminal.kind}' for ternary test block`,
testBlock.terminal.loc
);
const consequent = this.visitValueBlock(
testBlock.terminal.consequent,
@@ -895,10 +905,10 @@ class Driver {
};
}
default: {
invariant(
CompilerError.invariant(
false,
"Unexpected value block terminal kind '%s'",
terminal.kind
`Unexpected value block terminal kind '${terminal.kind}'`,
terminal.loc
);
}
}
@@ -914,7 +924,7 @@ class Driver {
): ReactiveTerminalStatement<ReactiveBreakTerminal> {
const target = this.cx.getBreakTarget(block);
if (target === null) {
invariant(false, "Expected a break target");
CompilerError.invariant(false, "Expected a break target", null);
}
switch (target.type) {
case "implicit": {
@@ -952,9 +962,10 @@ class Driver {
id: InstructionId
): ReactiveTerminalStatement<ReactiveContinueTerminal> {
const target = this.cx.getContinueTarget(block);
invariant(
CompilerError.invariant(
target !== null,
`Expected continue target to be scheduled for bb${block}`
`Expected continue target to be scheduled for bb${block}`,
null
);
switch (target.type) {
case "implicit": {
@@ -1035,9 +1046,10 @@ class Context {
*/
schedule(block: BlockId, type: "if" | "switch" | "case"): number {
const id = this.#nextScheduleId++;
invariant(
CompilerError.invariant(
!this.#scheduled.has(block),
`Break block is already scheduled: bb${block}`
`Break block is already scheduled: bb${block}`,
null
);
this.#scheduled.add(block);
this.#controlFlowStack.push({ block, id, type });
@@ -1052,9 +1064,10 @@ class Context {
const id = this.#nextScheduleId++;
const ownsBlock = !this.#scheduled.has(fallthroughBlock);
this.#scheduled.add(fallthroughBlock);
invariant(
CompilerError.invariant(
!this.#scheduled.has(continueBlock),
`Continue block is already scheduled: bb${continueBlock}`
`Continue block is already scheduled: bb${continueBlock}`,
null
);
this.#scheduled.add(continueBlock);
let ownsLoop = false;
@@ -1080,9 +1093,10 @@ class Context {
*/
unschedule(scheduleId: number): void {
const last = this.#controlFlowStack.pop();
invariant(
CompilerError.invariant(
last !== undefined && last.id === scheduleId,
"Can only unschedule the last target"
"Can only unschedule the last target",
null
);
if (last.type !== "loop" || last.ownsBlock !== null) {
this.#scheduled.delete(last.block);
@@ -6,7 +6,6 @@
*/
import * as t from "@babel/types";
import invariant from "invariant";
import { pruneUnusedLValues, pruneUnusedLabels, renameVariables } from ".";
import { CompilerError, ErrorSeverity } from "../CompilerError";
import { Environment } from "../HIR";
@@ -216,10 +215,10 @@ function codegenReactiveScope(
firstOutputIndex = index;
}
invariant(
CompilerError.invariant(
identifier.name != null,
"Expected identifier '@%s' to be named",
identifier.id
`Expected identifier '@${identifier.id}' to be named`,
null
);
const name = convertIdentifier(identifier);
@@ -284,10 +283,10 @@ function codegenReactiveScope(
null as t.Expression | null
);
if (testCondition === null) {
invariant(
CompilerError.invariant(
firstOutputIndex !== null,
"Expected scope '@%s' to have at least one declaration",
scope.id
`Expected scope '@${scope.id}' to have at least one declaration`,
null
);
testCondition = t.binaryExpression(
"===",
@@ -542,7 +541,11 @@ function codegenInstructionNullable(
]);
}
case InstructionKind.Reassign: {
invariant(value !== null, "Expected a value for reassignment");
CompilerError.invariant(
value !== null,
"Expected a value for reassignment",
instr.value.loc
);
const expr = t.assignmentExpression("=", codegenLValue(lvalue), value);
if (instr.lvalue !== null) {
if (instr.value.kind !== "StoreContext") {
@@ -589,9 +592,10 @@ function codegenForInit(
}))
).body;
const declaration = body[0]!;
invariant(
CompilerError.invariant(
declaration.type === "VariableDeclaration",
"Expected a variable declaration"
"Expected a variable declaration",
declaration.loc ?? null
);
return declaration;
} else {
@@ -740,9 +744,10 @@ function codegenInstructionValue(
switch (optionalValue.type) {
case "OptionalCallExpression":
case "CallExpression": {
invariant(
CompilerError.invariant(
t.isExpression(optionalValue.callee),
"v8 intrinsics are validated during lowering"
"v8 intrinsics are validated during lowering",
optionalValue.callee.loc ?? null
);
value = t.optionalCallExpression(
optionalValue.callee,
@@ -754,9 +759,10 @@ function codegenInstructionValue(
case "OptionalMemberExpression":
case "MemberExpression": {
const property = optionalValue.property;
invariant(
CompilerError.invariant(
t.isExpression(property),
"Private names are validated during lowering"
"Private names are validated during lowering",
property.loc ?? null
);
value = t.optionalMemberExpression(
optionalValue.object,
@@ -779,19 +785,21 @@ function codegenInstructionValue(
}
case "MethodCall": {
const memberExpr = codegenPlace(cx, instrValue.property);
invariant(
CompilerError.invariant(
t.isMemberExpression(memberExpr) ||
t.isOptionalMemberExpression(memberExpr),
"[Codegen] Internal error: MethodCall::property must be an unpromoted + unmemoized MemberExpression. " +
`Got a '${memberExpr.type}'`
`Got a '${memberExpr.type}'`,
memberExpr.loc ?? null
);
invariant(
CompilerError.invariant(
t.isNodesEquivalent(
memberExpr.object,
codegenPlace(cx, instrValue.receiver)
),
"[Codegen] Internal error: Forget should always generate MethodCall::property " +
"as a MemberExpression of MethodCall::receiver"
"as a MemberExpression of MethodCall::receiver",
memberExpr.loc ?? null
);
const args = instrValue.args.map((arg) => codegenArgument(cx, arg));
value = createCallExpression(instrValue.loc, memberExpr, args);
@@ -843,10 +851,10 @@ function codegenInstructionValue(
} else if (tagValue.type === "MemberExpression") {
tag = convertMemberExpressionToJsx(tagValue);
} else {
invariant(
CompilerError.invariant(
tagValue.type === "StringLiteral",
"Expected JSX tag to be an identifier or string, got '%s'",
tagValue.type
`Expected JSX tag to be an identifier or string, got '${tagValue.type}'`,
tagValue.loc ?? null
);
if (tagValue.value.indexOf(":") >= 0) {
const [namespace, name] = tagValue.value.split(":", 2);
@@ -1185,17 +1193,19 @@ function codegenJsxElement(
function convertMemberExpressionToJsx(
expr: t.MemberExpression
): t.JSXMemberExpression {
invariant(
CompilerError.invariant(
expr.property.type === "Identifier",
"Expected JSX member expression property to be a string"
"Expected JSX member expression property to be a string",
expr.loc ?? null
);
const property = t.jsxIdentifier(expr.property.name);
if (expr.object.type === "Identifier") {
return t.jsxMemberExpression(t.jsxIdentifier(expr.object.name), property);
} else {
invariant(
CompilerError.invariant(
expr.object.type === "MemberExpression",
"Expected JSX member expression to be an identifier or nested member expression"
"Expected JSX member expression to be an identifier or nested member expression",
expr.object.loc ?? null
);
const object = convertMemberExpressionToJsx(expr.object);
return t.jsxMemberExpression(object, property);
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import { Identifier, ReactiveScopeDependency } from "../HIR";
import { printIdentifier } from "../HIR/PrintHIR";
import { assertExhaustive } from "../Utils/utils";
@@ -122,11 +122,12 @@ export class ReactiveScopeDependencyTree {
const results = new Set<ReactiveScopeDependency>();
for (const [rootId, rootNode] of this.#roots.entries()) {
const deps = deriveMinimalDependenciesInSubtree(rootNode);
invariant(
CompilerError.invariant(
deps.every(
(dep) => dep.accessType === PropertyAccessType.UnconditionalDependency
),
"[PropagateScopeDependencies] All dependencies must be reduced to unconditional dependencies."
"[PropagateScopeDependencies] All dependencies must be reduced to unconditional dependencies.",
null
);
for (const dep of deps) {
@@ -162,9 +163,10 @@ export class ReactiveScopeDependencyTree {
promoteDepsFromExhaustiveConditionals(
trees: Array<ReactiveScopeDependencyTree>
): void {
invariant(
CompilerError.invariant(
trees.length > 1,
"Expected trees to be at least 2 elements long."
"Expected trees to be at least 2 elements long.",
null
);
for (const [id, root] of this.#roots) {
@@ -456,16 +458,18 @@ function addSubtreeIntersection(
otherProperties: Array<Map<string, DependencyNode>>,
currProperties: Map<string, DependencyNode>
): void {
invariant(
CompilerError.invariant(
otherProperties.length > 1,
"[DeriveMinimalDependencies] Expected otherProperties to be at least 2 elements long."
"[DeriveMinimalDependencies] Expected otherProperties to be at least 2 elements long.",
null
);
otherProperties.forEach((properties) =>
properties.forEach((node, _) =>
invariant(
CompilerError.invariant(
!isUnconditional(node.accessType),
"[DeriveMinimalDependencies] Expected otherProperties to only contain unconditional nodes!"
"[DeriveMinimalDependencies] Expected otherProperties to only contain unconditional nodes!",
null
)
)
);
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import {
ReactiveFunction,
ReactiveScopeBlock,
@@ -226,7 +226,11 @@ function printTerminal(writer: Writer, terminal: ReactiveTerminal): void {
writer.writeLine(`${prefix}: {`);
writer.indented(() => {
const block = case_.block;
invariant(block != null, "Expected case to have a block");
CompilerError.invariant(
block != null,
"Expected case to have a block",
case_.test?.loc ?? null
);
printReactiveInstructions(writer, block);
});
writer.writeLine("}");
@@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import prettyFormat from "pretty-format";
import { CompilerError } from "../CompilerError";
import {
@@ -244,9 +243,10 @@ class State {
this.scopes.set(scope.id, node);
}
const identifierNode = this.identifiers.get(identifier);
invariant(
CompilerError.invariant(
identifierNode !== undefined,
"Expected identifier to be initialized"
"Expected identifier to be initialized",
place.loc
);
identifierNode.scopes.add(scope.id);
}
@@ -264,9 +264,10 @@ function computeMemoizedIdentifiers(state: State): Set<IdentifierId> {
// Visit an identifier, optionally forcing it to be memoized
function visit(id: IdentifierId, forceMemoize: boolean = false): boolean {
const node = state.identifiers.get(id);
invariant(
CompilerError.invariant(
node !== undefined,
`Expected a node for all identifiers, none found for '${id}'`
`Expected a node for all identifiers, none found for '${id}'`,
null
);
if (node.seen) {
return node.memoized;
@@ -302,7 +303,11 @@ function computeMemoizedIdentifiers(state: State): Set<IdentifierId> {
// Force all the scope's optionally-memoizeable dependencies (not "Never") to be memoized
function forceMemoizeScopeDependencies(id: ScopeId): void {
const node = state.scopes.get(id);
invariant(node !== undefined, "Expected a node for all scopes");
CompilerError.invariant(
node !== undefined,
"Expected a node for all scopes",
null
);
if (node.seen) {
return;
}
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import {
Identifier,
IdentifierId,
@@ -103,6 +103,6 @@ class Scopes {
this.#stack.push(next);
fn();
const last = this.#stack.pop();
invariant(last === next, "Mismatch push/pop calls");
CompilerError.invariant(last === next, "Mismatch push/pop calls", null);
}
}
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import { BlockId, HIRFunction, Identifier, Place } from "../HIR/HIR";
import {
eachInstructionLValue,
@@ -83,7 +83,11 @@ export function eliminateRedundantPhi(fn: HIRFunction): void {
same = operand;
}
}
invariant(same !== null, "Expected phis to be non-empty");
CompilerError.invariant(
same !== null,
"Expected phis to be non-empty",
null
);
rewrites.set(phi.id, same);
block.phis.delete(phi);
}
@@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import { Environment } from "../HIR/Environment";
import {
@@ -68,9 +67,10 @@ class SSABuilder {
}
state(): State {
invariant(
CompilerError.invariant(
this.#current !== null,
"we need to be in a block to access state!"
"we need to be in a block to access state!",
null
);
return this.#states.get(this.#current)!;
}
@@ -237,9 +237,10 @@ function enterSSAImpl(
): void {
const visitedBlocks: Set<BasicBlock> = new Set();
for (const [blockId, block] of func.body.blocks) {
invariant(
CompilerError.invariant(
!visitedBlocks.has(block),
`found a cycle! visiting bb${block.id} again`
`found a cycle! visiting bb${block.id} again`,
null
);
visitedBlocks.add(block);
@@ -269,9 +270,10 @@ function enterSSAImpl(
) {
const loweredFunc = instr.value.loweredFunc;
const entry = loweredFunc.body.blocks.get(loweredFunc.body.entry)!;
invariant(
CompilerError.invariant(
entry.preds.size === 0,
"Expected function expression entry block to have zero predecessors"
"Expected function expression entry block to have zero predecessors",
null
);
entry.preds.add(blockId);
builder.defineFunction(loweredFunc);
@@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import {
BasicBlock,
@@ -220,7 +219,11 @@ export function leaveSSA(fn: HIRFunction): void {
}
}
}
invariant(kind !== null, "Expected at least one operand");
CompilerError.invariant(
kind !== null,
"Expected at least one operand",
null
);
value.lvalue.kind = kind;
}
rewritePlace(lvalue, rewrites, declarations);
@@ -353,9 +356,10 @@ export function leaveSSA(fn: HIRFunction): void {
// If we never saw a declaration for this phi, it may have been pruned by DCE, so synthesize
// a new Let binding
invariant(
CompilerError.invariant(
phi.id.name != null,
"Expected reassignment phis to have a name"
"Expected reassignment phis to have a name",
null
);
const declaration = declarations.get(phi.id.name);
if (declaration === undefined) {
@@ -6,7 +6,7 @@
*/
import * as t from "@babel/types";
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
import { Environment } from "../HIR";
import {
HIRFunction,
@@ -342,7 +342,11 @@ class Unifier {
if (type.kind === "Phi") {
const operands = new Set(type.operands.map((i) => this.get(i).kind));
invariant(operands.size > 0, "there should be at least one operand");
CompilerError.invariant(
operands.size > 0,
"there should be at least one operand",
null
);
const kind = operands.values().next().value;
// there's only one unique type and it's not a type var
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import { CompilerError } from "../CompilerError";
/**
* Represents items which form disjoint sets.
@@ -20,7 +20,11 @@ export default class DisjointSet<T> {
*/
union(items: Array<T>): void {
const first = items.shift();
invariant(first != null, "Expected set to be non-empty");
CompilerError.invariant(
first != null,
"Expected set to be non-empty",
null
);
// determine an arbitrary "root" for this set: if the first
// item already has a root then use that, otherwise the first item
// will be the new root.
@@ -18,7 +18,7 @@ function useFoo(props) {
## Error
```
[InferReferenceEffects] Context variables are always mutable.
[ReactForget] Invariant: [InferReferenceEffects] Context variables are always mutable. (5:5)
```