Use type assertions to drive inference

This PR uses the information from type cast expressions (`as`  or `(variable: 
type)`)  to inform type inference. BuildHIR converts the type annotation to our 
internal type format where possible, falling back to the generic `makeType()`. 
This is then used in InferTypes to help set the value's type.
This commit is contained in:
Joe Savona
2023-12-11 11:34:28 -08:00
parent d163c0b7c3
commit 8b3898c164
10 changed files with 124 additions and 53 deletions
@@ -41,10 +41,12 @@ import {
SourceLocation,
SpreadPattern,
ThrowTerminal,
Type,
makeInstructionId,
makeType,
} from "./HIR";
import HIRBuilder, { Bindings } from "./HIRBuilder";
import { BuiltInArrayId } from "./ObjectShape";
/*
* *******************************************************************************************
@@ -2148,21 +2150,23 @@ function lowerExpression(
}
case "TypeCastExpression": {
let expr = exprPath as NodePath<t.TypeCastExpression>;
const typeAnnotation = expr.get("typeAnnotation").get("typeAnnotation");
return {
kind: "TypeCastExpression",
value: lowerExpressionToTemporary(builder, expr.get("expression")),
typeAnnotation: expr.get("typeAnnotation").get("typeAnnotation").node,
type: makeType(),
typeAnnotation: typeAnnotation.node,
type: lowerType(builder, typeAnnotation),
loc: exprLoc,
};
}
case "TSAsExpression": {
let expr = exprPath as NodePath<t.TSAsExpression>;
const typeAnnotation = expr.get("typeAnnotation");
return {
kind: "TypeCastExpression",
value: lowerExpressionToTemporary(builder, expr.get("expression")),
typeAnnotation: expr.get("typeAnnotation").node,
type: makeType(),
typeAnnotation: typeAnnotation.node,
type: lowerType(builder, typeAnnotation),
loc: exprLoc,
};
}
@@ -3791,3 +3795,52 @@ function gatherCapturedDeps(
function notNull<T>(value: T | null): value is T {
return value !== null;
}
function lowerType(
_builder: HIRBuilder,
path: NodePath<t.FlowType | t.TSType>
): Type {
const node = path.node;
switch (node.type) {
case "GenericTypeAnnotation": {
const typeAnnotation = path as NodePath<t.GenericTypeAnnotation>;
const id = typeAnnotation.get("id");
if (id.node.type === "Identifier" && id.node.name === "Array") {
return { kind: "Object", shapeId: BuiltInArrayId };
}
return makeType();
}
case "TSTypeReference": {
const typeReference = path as NodePath<t.TSTypeReference>;
const typeName = typeReference.get("typeName").node;
if (typeName.type === "Identifier" && typeName.name === "Array") {
return { kind: "Object", shapeId: BuiltInArrayId };
}
return makeType();
}
case "ArrayTypeAnnotation":
case "TSArrayType": {
return { kind: "Object", shapeId: BuiltInArrayId };
}
case "BooleanLiteralTypeAnnotation":
case "BooleanTypeAnnotation":
case "NullLiteralTypeAnnotation":
case "NumberLiteralTypeAnnotation":
case "NumberTypeAnnotation":
case "StringLiteralTypeAnnotation":
case "StringTypeAnnotation":
case "TSBooleanKeyword":
case "TSNullKeyword":
case "TSNumberKeyword":
case "TSStringKeyword":
case "TSSymbolKeyword":
case "TSUndefinedKeyword":
case "TSVoidKeyword":
case "VoidTypeAnnotation": {
return { kind: "Primitive" };
}
default: {
return makeType();
}
}
}
@@ -373,7 +373,9 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
break;
}
case "TypeCastExpression": {
value = `TypeCast ${printPlace(instrValue.value)}`;
value = `TypeCast ${printPlace(instrValue.value)}: ${printType(
instrValue.type
)}`;
break;
}
case "JsxExpression": {
@@ -262,7 +262,8 @@ function* generateInstructionTypes(
}
case "TypeCastExpression": {
yield equation(left, value.value.identifier.type);
yield equation(value.type, value.value.identifier.type);
yield equation(left, value.type);
break;
}
@@ -2,14 +2,16 @@
## Input
```javascript
import { identity } from "shared-runtime";
function Component(props: { id: number }) {
const x = [props.id] as number[];
const y = identity(x[0]);
const x = makeArray(props.id) as number[];
const y = x.at(0);
return y;
}
function makeArray<T>(x: T): Array<T> {
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ id: 42 }],
@@ -21,23 +23,42 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
import { identity } from "shared-runtime";
function Component(props) {
const $ = useMemoCache(2);
const $ = useMemoCache(4);
let t0;
if ($[0] !== props.id) {
const x = [props.id] as number[];
t0 = identity(x[0]);
t0 = makeArray(props.id);
$[0] = props.id;
$[1] = t0;
} else {
t0 = $[1];
}
const y = t0;
const x = t0 as number[];
let t1;
if ($[2] !== x) {
t1 = x.at(0);
$[2] = x;
$[3] = t1;
} else {
t1 = $[3];
}
const y = t1;
return y;
}
function makeArray(x) {
const $ = useMemoCache(2);
let t0;
if ($[0] !== x) {
t0 = [x];
$[0] = x;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ id: 42 }],
@@ -1,11 +1,13 @@
import { identity } from "shared-runtime";
function Component(props: { id: number }) {
const x = [props.id] as number[];
const y = identity(x[0]);
const x = makeArray(props.id) as number[];
const y = x.at(0);
return y;
}
function makeArray<T>(x: T): Array<T> {
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ id: 42 }],
@@ -3,11 +3,11 @@
```javascript
// @flow
import { identity } from "shared-runtime";
import { identity, makeArray } from "shared-runtime";
function Component(props: { id: number }) {
const x = ([props.id]: Array<number>);
const y = identity(x[0]);
const x = (makeArray(props.id): Array<number>);
const y = x.at(0);
return y;
}
@@ -22,20 +22,28 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
import { identity } from "shared-runtime";
import { identity, makeArray } from "shared-runtime";
function Component(props) {
const $ = useMemoCache(2);
const $ = useMemoCache(4);
let t0;
if ($[0] !== props.id) {
const x = ([props.id]: Array<number>);
t0 = identity(x[0]);
t0 = makeArray(props.id);
$[0] = props.id;
$[1] = t0;
} else {
t0 = $[1];
}
const y = t0;
const x = (t0: Array<number>);
let t1;
if ($[2] !== x) {
t1 = x.at(0);
$[2] = x;
$[3] = t1;
} else {
t1 = $[3];
}
const y = t1;
return y;
}
@@ -1,9 +1,9 @@
// @flow
import { identity } from "shared-runtime";
import { identity, makeArray } from "shared-runtime";
function Component(props: { id: number }) {
const x = ([props.id]: Array<number>);
const y = identity(x[0]);
const x = (makeArray(props.id): Array<number>);
const y = x.at(0);
return y;
}
@@ -20,20 +20,10 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
import { identity } from "shared-runtime";
function Component(props) {
const $ = useMemoCache(2);
let t0;
if ($[0] !== props.id) {
t0 = identity(props.id);
$[0] = props.id;
$[1] = t0;
} else {
t0 = $[1];
}
const x = t0;
const x = identity(props.id);
const y = x as number;
return y;
}
@@ -21,20 +21,10 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
import { identity } from "shared-runtime";
function Component(props) {
const $ = useMemoCache(2);
let t0;
if ($[0] !== props.id) {
t0 = identity(props.id);
$[0] = props.id;
$[1] = t0;
} else {
t0 = $[1];
}
const x = t0;
const x = identity(props.id);
const y = (x: number);
return y;
}
@@ -127,6 +127,10 @@ export function makeObject_Primitives(): StringKeyedObject {
return { a: 0, b: "value1", c: true };
}
export function makeArray<T>(value: T): Array<T> {
return [value];
}
export function addOne(value: number): number {
return value + 1;
}