Skip to content

First-class functions #1384

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
362 changes: 217 additions & 145 deletions src/builtins.ts

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ export namespace CommonNames {
export const StaticArray = "StaticArray";
export const Set = "Set";
export const Map = "Map";
export const Function = "Function";
export const ArrayBufferView = "ArrayBufferView";
export const ArrayBuffer = "ArrayBuffer";
export const Math = "Math";
Expand Down
829 changes: 433 additions & 396 deletions src/compiler.ts

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ export class IDLBuilder extends ExportsWalker {
// if (i >= requiredParameters) sb.push("optional ");
sb.push(this.typeToString(parameters[i]));
sb.push(" ");
sb.push(signature.getParameterName(i));
sb.push(element.getParameterName(i));
}
sb.push(");\n");
var members = element.members;
Expand Down Expand Up @@ -463,7 +463,7 @@ export class TSDBuilder extends ExportsWalker {
for (let i = 0; i < numParameters; ++i) {
if (i) sb.push(", ");
// if (i >= requiredParameters) sb.push("optional ");
sb.push(signature.getParameterName(i));
sb.push(element.getParameterName(i));
sb.push(": ");
sb.push(this.typeToString(parameters[i]));
}
Expand Down
22 changes: 12 additions & 10 deletions src/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -951,12 +951,12 @@ export class Flow {
for (let i = 0, k = min<i32>(numThisLocalFlags, numOtherLocalFlags); i < k; ++i) {
let local = localsByIndex[i];
let type = local.type;
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) {
if (type.isShortIntegerValue) {
if (before.isLocalFlag(i, LocalFlags.WRAPPED) && !after.isLocalFlag(i, LocalFlags.WRAPPED)) {
return true;
}
}
if (type.is(TypeFlags.REFERENCE)) {
if (type.isNullableReference) {
if (before.isLocalFlag(i, LocalFlags.NONNULL) && !after.isLocalFlag(i, LocalFlags.NONNULL)) {
return true;
}
Expand Down Expand Up @@ -986,19 +986,19 @@ export class Flow {

/** Checks if an expression of the specified type is known to be non-null, even if the type might be nullable. */
isNonnull(expr: ExpressionRef, type: Type): bool {
if (!type.is(TypeFlags.NULLABLE)) return true;
if (!type.isNullableReference) return true;
// below, only teeLocal/getLocal are relevant because these are the only expressions that
// depend on a dynamic nullable state (flag = LocalFlags.NONNULL), while everything else
// has already been handled by the nullable type check above.
switch (getExpressionId(expr)) {
case ExpressionId.LocalSet: {
if (!isLocalTee(expr)) break;
let local = this.parentFunction.localsByIndex[getLocalSetIndex(expr)];
return !local.type.is(TypeFlags.NULLABLE) || this.isLocalFlag(local.index, LocalFlags.NONNULL, false);
return !local.type.isNullableReference || this.isLocalFlag(local.index, LocalFlags.NONNULL, false);
}
case ExpressionId.LocalGet: {
let local = this.parentFunction.localsByIndex[getLocalGetIndex(expr)];
return !local.type.is(TypeFlags.NULLABLE) || this.isLocalFlag(local.index, LocalFlags.NONNULL, false);
return !local.type.isNullableReference || this.isLocalFlag(local.index, LocalFlags.NONNULL, false);
}
}
return false;
Expand Down Expand Up @@ -1219,7 +1219,7 @@ export class Flow {
assert(type != Type.void);

// types other than i8, u8, i16, u16 and bool do not overflow
if (!type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) return false;
if (!type.isShortIntegerValue) return false;

var operand: ExpressionRef;
switch (getExpressionId(expr)) {
Expand Down Expand Up @@ -1347,7 +1347,7 @@ export class Flow {
// wrapped, it can't overflow.
case BinaryOp.ShrU32: {
let shift = 32 - type.size;
return type.is(TypeFlags.SIGNED)
return type.isSignedIntegerValue
? !(
getExpressionId(operand = getBinaryRight(expr)) == ExpressionId.Const &&
getConstValueI32(operand) > shift // must clear MSB
Expand Down Expand Up @@ -1492,9 +1492,11 @@ export class Flow {

/** Tests if a conversion from one type to another can technically overflow. */
function canConversionOverflow(fromType: Type, toType: Type): bool {
return !fromType.is(TypeFlags.INTEGER) // non-i32 locals or returns
|| fromType.size > toType.size
|| fromType.is(TypeFlags.SIGNED) != toType.is(TypeFlags.SIGNED);
return toType.isShortIntegerValue && (
!fromType.isIntegerValue || // i.e. float to small int
fromType.size > toType.size || // larger int to small int
fromType.isSignedIntegerValue != toType.isSignedIntegerValue // signedness mismatch
);
}

/** Finds all indexes of locals used in the specified expression. */
Expand Down
27 changes: 14 additions & 13 deletions src/glue/js/i64.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ declare type i64 = { __Long__: true }; // opaque
declare const i64_zero: i64;
declare const i64_one: i64;

declare function i64_is(value: unknown): value is i64;
declare function i64_new(lo: i32, hi?: i32): i64;
declare function i64_low(value: i64): i32;
declare function i64_high(value: i64): i32;
Expand All @@ -28,22 +29,22 @@ declare function i64_shr(left: i64, right: i64): i64;
declare function i64_shr_u(left: i64, right: i64): i64;
declare function i64_not(value: i64): i64;

declare function i64_eq(left: i64, right: i64): bool;
declare function i64_ne(left: i64, right: i64): bool;
declare function i64_gt(left: i64, right: i64): bool;
declare function i64_eq(left: i64, right: i64): boolean;
declare function i64_ne(left: i64, right: i64): boolean;
declare function i64_gt(left: i64, right: i64): boolean;

declare function i64_align(value: i64, alignment: i32): i64;

declare function i64_is_i8(value: i64): bool;
declare function i64_is_i16(value: i64): bool;
declare function i64_is_i32(value: i64): bool;
declare function i64_is_u8(value: i64): bool;
declare function i64_is_u16(value: i64): bool;
declare function i64_is_u32(value: i64): bool;
declare function i64_is_bool(value: i64): bool;
declare function i64_is_f32(value: i64): bool;
declare function i64_is_f64(value: i64): bool;
declare function i64_is_i8(value: i64): boolean;
declare function i64_is_i16(value: i64): boolean;
declare function i64_is_i32(value: i64): boolean;
declare function i64_is_u8(value: i64): boolean;
declare function i64_is_u16(value: i64): boolean;
declare function i64_is_u32(value: i64): boolean;
declare function i64_is_bool(value: i64): boolean;
declare function i64_is_f32(value: i64): boolean;
declare function i64_is_f64(value: i64): boolean;

declare function i64_to_f32(value: i64): f64;
declare function i64_to_f64(value: i64): f64;
declare function i64_to_string(value: i64, unsigned?: bool): string;
declare function i64_to_string(value: i64, unsigned?: boolean): string;
4 changes: 4 additions & 0 deletions src/glue/js/i64.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ global.i64_zero = Long.ZERO;
global.i64_one = Long.ONE;
global.i64_neg_one = Long.fromInt(-1);

global.i64_is = function i64_is(value) {
return Long.isLong(value);
};

global.i64_new = function i64_new(lo, hi) {
return Long.fromBits(lo, hi);
};
Expand Down
7 changes: 7 additions & 0 deletions src/glue/wasm/i64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

/* eslint-disable @typescript-eslint/no-unused-vars */

// @ts-ignore: decorator
@global const i64_zero: i64 = 0;

// @ts-ignore: decorator
Expand All @@ -13,6 +14,12 @@
// @ts-ignore: decorator
@global const i64_neg_one: i64 = -1;

// @ts-ignore: decorator
@global @inline
function i64_is<T>(value: T): bool {
return isInteger<T>() && sizeof<T>() == 8;
}

// @ts-ignore: decorator
@global @inline
function i64_new(lo: i32, hi: i32 = 0): i64 {
Expand Down
7 changes: 5 additions & 2 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1563,13 +1563,14 @@ export class Parser extends DiagnosticEmitter {
var parameters = this.parseParameters(tn);
if (!parameters) return null;

return this.parseFunctionExpressionCommon(tn, name, parameters, arrowKind, startPos, signatureStart);
return this.parseFunctionExpressionCommon(tn, name, parameters, this.parseParametersThis, arrowKind, startPos, signatureStart);
}

private parseFunctionExpressionCommon(
tn: Tokenizer,
name: IdentifierExpression,
parameters: ParameterNode[],
explicitThis: NamedTypeNode | null,
arrowKind: ArrowKind,
startPos: i32 = -1,
signatureStart: i32 = -1
Expand Down Expand Up @@ -1598,7 +1599,7 @@ export class Parser extends DiagnosticEmitter {
var signature = Node.createFunctionType(
parameters,
returnType,
null, // TODO?
explicitThis,
false,
tn.range(signatureStart, tn.pos)
);
Expand Down Expand Up @@ -3585,6 +3586,7 @@ export class Parser extends DiagnosticEmitter {
tn,
Node.createEmptyIdentifierExpression(tn.range(startPos)),
[],
null,
ArrowKind.ARROW_PARENTHESIZED
);
}
Expand Down Expand Up @@ -3777,6 +3779,7 @@ export class Parser extends DiagnosticEmitter {
identifier.range
)
],
null,
ArrowKind.ARROW_SINGLE,
startPos
);
Expand Down
Loading