From 3ab89ab394ec11a43a62c9db99bfa606f882e95e Mon Sep 17 00:00:00 2001 From: dcode Date: Thu, 3 Oct 2019 08:06:44 +0200 Subject: [PATCH] Parse optional chaining and nullish coalescing --- package-lock.json | 6 +- package.json | 2 +- src/ast.ts | 35 +++++ src/common.ts | 5 +- src/compiler.ts | 97 +++++++++++-- src/extra/ast.ts | 16 ++- src/parser.ts | 127 +++++++++++++----- src/program.ts | 3 +- src/tokenizer.ts | 18 +++ std/assembly/builtins.ts | 38 +++--- std/assembly/index.d.ts | 2 + tests/parser/nullish-coalescing.ts | 1 + tests/parser/nullish-coalescing.ts.fixture.ts | 1 + tests/parser/optional-chaining.ts | 12 ++ tests/parser/optional-chaining.ts.fixture.ts | 9 ++ 15 files changed, 299 insertions(+), 73 deletions(-) create mode 100644 tests/parser/nullish-coalescing.ts create mode 100644 tests/parser/nullish-coalescing.ts.fixture.ts create mode 100644 tests/parser/optional-chaining.ts create mode 100644 tests/parser/optional-chaining.ts.fixture.ts diff --git a/package-lock.json b/package-lock.json index fccff050c9..f78646f107 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4257,9 +4257,9 @@ "dev": true }, "typescript": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", - "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==", + "version": "3.7.0-beta", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.0-beta.tgz", + "integrity": "sha512-4jyCX+IQamrPJxgkABPq9xf+hUN+GWHVxoj+oey1TadCPa4snQl1RKwUba+1dyzYCamwlCxKvZQ3TjyWLhMGBA==", "dev": true }, "union-value": { diff --git a/package.json b/package.json index d008faa551..401b8f11cd 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "ts-node": "^6.2.0", "tslint": "^5.20.0", "typedoc-plugin-external-module-name": "^2.1.0", - "typescript": "^3.6.3", + "typescript": "^3.7.0-beta", "webpack": "^4.40.2", "webpack-cli": "^3.3.9" }, diff --git a/src/ast.ts b/src/ast.ts index 6dc0ede3ce..880c5bc05c 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -53,6 +53,7 @@ export enum NodeKind { PARENTHESIZED, PROPERTYACCESS, TERNARY, + COALESCE, SUPER, THIS, TRUE, @@ -328,6 +329,7 @@ export abstract class Node { expression: Expression, typeArgs: TypeNode[] | null, args: Expression[], + isOptionalChaining: bool, range: Range ): CallExpression { var expr = new CallExpression(); @@ -335,6 +337,7 @@ export abstract class Node { expr.expression = expression; expr.typeArguments = typeArgs; expr.arguments = args; + expr.isOptionalChaining = isOptionalChaining; return expr; } @@ -368,12 +371,14 @@ export abstract class Node { static createElementAccessExpression( expression: Expression, element: Expression, + isOptionalChaining: bool, range: Range ): ElementAccessExpression { var expr = new ElementAccessExpression(); expr.range = range; expr.expression = expression; expr.elementExpression = element; + expr.isOptionalChaining = isOptionalChaining; return expr; } @@ -448,6 +453,18 @@ export abstract class Node { return expr; } + static createCoalesceExpression( + ifThen: Expression, + ifElse: Expression, + range: Range + ): CoalesceExpression { + var expr = new CoalesceExpression(); + expr.range = range; + expr.ifThen = ifThen; + expr.ifElse = ifElse; + return expr; + } + static createObjectLiteralExpression( names: IdentifierExpression[], values: Expression[], @@ -473,12 +490,14 @@ export abstract class Node { static createPropertyAccessExpression( expression: Expression, property: IdentifierExpression, + isOptionalChaining: bool, range: Range ): PropertyAccessExpression { var expr = new PropertyAccessExpression(); expr.range = range; expr.expression = expression; expr.property = property; + expr.isOptionalChaining = isOptionalChaining; return expr; } @@ -1387,6 +1406,8 @@ export class CallExpression extends Expression { typeArguments: TypeNode[] | null; /** Provided arguments. */ arguments: Expression[]; + /** Whether optional chaining is used between the expression and the signature. */ + isOptionalChaining: bool; /** Gets the type arguments range for reporting. */ get typeArgumentsRange(): Range { @@ -1417,6 +1438,16 @@ export class ClassExpression extends Expression { declaration: ClassDeclaration; } +/** Represents a nullish coalescing expression. */ +export class CoalesceExpression extends Expression { + kind = NodeKind.COALESCE; + + /** Expression head checked for being nullish. */ + ifThen: Expression; + /** Expression executed when `ifThen` is nullish. */ + ifElse: Expression; +} + /** Represents a comma expression composed of multiple expressions. */ export class CommaExpression extends Expression { kind = NodeKind.COMMA; @@ -1440,6 +1471,8 @@ export class ElementAccessExpression extends Expression { expression: Expression; /** Element of the expression being accessed. */ elementExpression: Expression; + /** Whether optional chaining is used between the expression and the access. */ + isOptionalChaining: bool; } /** Represents a float literal expression. */ @@ -1514,6 +1547,8 @@ export class PropertyAccessExpression extends Expression { expression: Expression; /** Property of the expression being accessed. */ property: IdentifierExpression; + /** Whether optional chaining is used between the expression and the property. */ + isOptionalChaining: bool; } /** Represents a regular expression literal expression. */ diff --git a/src/common.ts b/src/common.ts index 4c8106a25a..08de6122d5 100644 --- a/src/common.ts +++ b/src/common.ts @@ -70,13 +70,11 @@ export enum CommonFlags { TRAMPOLINE = 1 << 25, /** Is a virtual method. */ VIRTUAL = 1 << 26, - /** Is the main function. */ - MAIN = 1 << 27, // Other /** Is quoted. */ - QUOTED = 1 << 28 + QUOTED = 1 << 27 } /** Path delimiter inserted between file system levels. */ @@ -133,6 +131,7 @@ export namespace CommonSymbols { export const void_ = "void"; export const number = "number"; export const boolean = "boolean"; + export const auto = "auto"; export const string = "string"; export const native = "native"; export const indexof = "indexof"; diff --git a/src/compiler.ts b/src/compiler.ts index 3d617dbd9b..6cd9179aab 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -106,6 +106,7 @@ import { Range, DecoratorKind, AssertionKind, + SourceKind, Statement, BlockStatement, @@ -135,6 +136,7 @@ import { WhileStatement, Expression, + ExportDefaultStatement, AssertionExpression, BinaryExpression, CallExpression, @@ -151,6 +153,7 @@ import { ParenthesizedExpression, PropertyAccessExpression, TernaryExpression, + CoalesceExpression, ArrayLiteralExpression, StringLiteralExpression, UnaryPostfixExpression, @@ -158,9 +161,7 @@ import { nodeIsConstantValue, findDecorator, - isTypeOmitted, - ExportDefaultStatement, - SourceKind + isTypeOmitted } from "./ast"; import { @@ -1146,7 +1147,7 @@ export class Compiler extends DiagnosticEmitter { assert(instance.prototype.arrowKind); // none of the following can be an arrow function - assert(!instance.isAny(CommonFlags.CONSTRUCTOR | CommonFlags.GET | CommonFlags.SET | CommonFlags.MAIN)); + assert(!instance.isAny(CommonFlags.CONSTRUCTOR | CommonFlags.GET | CommonFlags.SET)); let expr = this.compileExpression((bodyNode).expression, returnType, Constraints.CONV_IMPLICIT @@ -2849,6 +2850,10 @@ export class Compiler extends DiagnosticEmitter { expr = this.compileTernaryExpression(expression, contextualType, constraints); break; } + case NodeKind.COALESCE: { + expr = this.compileCoalesceExpression(expression, contextualType, constraints); + break; + } case NodeKind.UNARYPOSTFIX: { expr = this.compileUnaryPostfixExpression(expression, contextualType, constraints); break; @@ -2892,9 +2897,13 @@ export class Compiler extends DiagnosticEmitter { contextualType: Type, constraints: Constraints = Constraints.NONE ): ExpressionRef { - return this.module.precomputeExpression( - this.compileExpression(expression, contextualType, constraints) - ); + var orig = this.compileExpression(expression, contextualType, constraints); + var expr = this.module.precomputeExpression(orig); + if (orig != expr) { + let skippedAutoreleases = this.skippedAutoreleases; + if (skippedAutoreleases.has(orig)) skippedAutoreleases.add(expr); + } + return expr; } convertExpression( @@ -5734,9 +5743,41 @@ export class Compiler extends DiagnosticEmitter { var module = this.module; var flow = this.currentFlow; + var targetExpression = expression.expression; + + // TODO: In these cases we compile the target of an element or property + // access, but not the element or property access itself, essentially + // skipping over optional chaining. + switch (targetExpression.kind) { + case NodeKind.ELEMENTACCESS: { + if ((targetExpression).isOptionalChaining) { + this.error( + DiagnosticCode.Not_implemented, + (targetExpression).expression.range.atEnd + ); + } + break; + } + case NodeKind.PROPERTYACCESS: { + if ((targetExpression).isOptionalChaining) { + this.error( + DiagnosticCode.Not_implemented, + (targetExpression).expression.range.atEnd + ); + } + break; + } + } + // TODO + if (expression.isOptionalChaining) { + this.error( + DiagnosticCode.Not_implemented, + targetExpression.range.atEnd + ); + } // handle call to super - if (expression.expression.kind == NodeKind.SUPER) { + if (targetExpression.kind == NodeKind.SUPER) { let flow = this.currentFlow; let actualFunction = flow.actualFunction; if (!actualFunction.is(CommonFlags.CONSTRUCTOR)) { @@ -5793,7 +5834,7 @@ export class Compiler extends DiagnosticEmitter { } // otherwise resolve normally - var target = this.resolver.lookupExpression(expression.expression, flow); // reports + var target = this.resolver.lookupExpression(targetExpression, flow); // reports if (!target) return module.unreachable(); var signature: Signature | null; @@ -5817,7 +5858,7 @@ export class Compiler extends DiagnosticEmitter { if (!prototype.is(CommonFlags.GENERIC)) { this.error( DiagnosticCode.Type_0_is_not_generic, - expression.expression.range, prototype.internalName + targetExpression.range, prototype.internalName ); return module.unreachable(); } @@ -5879,7 +5920,7 @@ export class Compiler extends DiagnosticEmitter { // invalid because the type is effectively unknown inside the function body this.error( DiagnosticCode.Type_argument_expected, - expression.expression.range.atEnd + targetExpression.range.atEnd ); return this.module.unreachable(); } @@ -5962,7 +6003,7 @@ export class Compiler extends DiagnosticEmitter { } case ElementKind.FUNCTION_TARGET: { signature = (target).signature; - indexArg = this.compileExpression(expression.expression, (target).type, Constraints.CONV_IMPLICIT); + indexArg = this.compileExpression(targetExpression, (target).type, Constraints.CONV_IMPLICIT); break; } @@ -5970,7 +6011,7 @@ export class Compiler extends DiagnosticEmitter { let getterPrototype = assert((target).getterPrototype); let getterInstance = this.resolver.resolveFunction(getterPrototype, null); if (!getterInstance) return module.unreachable(); - indexArg = this.compileCallDirect(getterInstance, [], expression.expression); + indexArg = this.compileCallDirect(getterInstance, [], targetExpression); signature = this.currentType.signatureReference; if (!signature) { this.error( @@ -5983,7 +6024,7 @@ export class Compiler extends DiagnosticEmitter { } case ElementKind.PROPERTY: { // instance property let getterInstance = assert((target).getterInstance); - indexArg = this.compileCallDirect(getterInstance, [], expression.expression, + indexArg = this.compileCallDirect(getterInstance, [], targetExpression, this.compileExpression(assert(this.resolver.currentThisExpression), this.options.usizeType) ); signature = this.currentType.signatureReference; @@ -6948,6 +6989,14 @@ export class Compiler extends DiagnosticEmitter { contextualType: Type, constraints: Constraints ): ExpressionRef { + + if (expression.isOptionalChaining) { + this.error( + DiagnosticCode.Not_implemented, + expression.expression.range.atEnd + ); + } + var module = this.module; var targetExpression = expression.expression; var targetType = this.resolver.resolveExpression(targetExpression, this.currentFlow); // reports @@ -7951,6 +8000,14 @@ export class Compiler extends DiagnosticEmitter { ctxType: Type, constraints: Constraints ): ExpressionRef { + + if (expression.isOptionalChaining) { + this.error( + DiagnosticCode.Not_implemented, + expression.expression.range.atEnd + ); + } + var module = this.module; var flow = this.currentFlow; @@ -8138,6 +8195,18 @@ export class Compiler extends DiagnosticEmitter { return expr; } + compileCoalesceExpression( + expression: CoalesceExpression, + ctxType: Type, + constraints: Constraints + ): ExpressionRef { + this.error( + DiagnosticCode.Not_implemented, + expression.range + ); + return this.module.unreachable(); + } + compileUnaryPostfixExpression( expression: UnaryPostfixExpression, contextualType: Type, diff --git a/src/extra/ast.ts b/src/extra/ast.ts index 384473196f..e90048aac5 100644 --- a/src/extra/ast.ts +++ b/src/extra/ast.ts @@ -38,6 +38,7 @@ import { ParenthesizedExpression, PropertyAccessExpression, TernaryExpression, + CoalesceExpression, UnaryPostfixExpression, UnaryExpression, UnaryPrefixExpression, @@ -200,6 +201,10 @@ export class ASTBuilder { this.visitTernaryExpression(node); break; } + case NodeKind.COALESCE: { + this.visitCoalesceExpression(node); + break; + } case NodeKind.UNARYPOSTFIX: { this.visitUnaryPostfixExpression(node); break; @@ -545,6 +550,7 @@ export class ASTBuilder { visitCallExpression(node: CallExpression): void { var sb = this.sb; this.visitNode(node.expression); + if (node.isOptionalChaining) sb.push("?."); var typeArguments = node.typeArguments; if (typeArguments) { let numTypeArguments = typeArguments.length; @@ -577,6 +583,13 @@ export class ASTBuilder { this.visitClassDeclaration(declaration); } + visitCoalesceExpression(node: CoalesceExpression): void { + var sb = this.sb; + this.visitNode(node.ifThen); + sb.push(" ?? "); + this.visitNode(node.ifElse); + } + visitCommaExpression(node: CommaExpression): void { var expressions = node.expressions; var numExpressions = assert(expressions.length); @@ -591,6 +604,7 @@ export class ASTBuilder { visitElementAccessExpression(node: ElementAccessExpression): void { var sb = this.sb; this.visitNode(node.expression); + if (node.isOptionalChaining) sb.push("?."); sb.push("["); this.visitNode(node.elementExpression); sb.push("]"); @@ -769,7 +783,7 @@ export class ASTBuilder { visitPropertyAccessExpression(node: PropertyAccessExpression): void { this.visitNode(node.expression); - this.sb.push("."); + this.sb.push(node.isOptionalChaining ? "?." : "."); this.visitIdentifierExpression(node.property); } diff --git a/src/parser.ts b/src/parser.ts index c12d964ee1..0d97fa2032 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -803,6 +803,7 @@ export class Parser extends DiagnosticEmitter { expression = Node.createPropertyAccessExpression( expression, Node.createIdentifierExpression(name, tn.range()), + false, tn.range(startPos, tn.pos) ); } else { @@ -3651,6 +3652,7 @@ export class Parser extends DiagnosticEmitter { (nextPrecedence = determinePrecedence(token = tn.peek())) >= precedence ) { // precedence climbing tn.next(); + let isOptionalChaining: bool = false; switch (token) { // AssertionExpression case Token.AS: { @@ -3698,6 +3700,7 @@ export class Parser extends DiagnosticEmitter { expr = Node.createElementAccessExpression( expr, next, + false, tn.range(startPos, tn.pos) ); break; @@ -3746,6 +3749,17 @@ export class Parser extends DiagnosticEmitter { ); break; } + // CoalesceExpression + case Token.QUESTION_QUESTION: { + let ifElse = this.parseExpression(tn); + if (!ifElse) return null; + expr = Node.createCoalesceExpression( + expr, + ifElse, + tn.range(startPos, tn.pos) + ); + break; + } // CommaExpression case Token.COMMA: { let commaExprs: Expression[] = [ expr ]; @@ -3757,38 +3771,55 @@ export class Parser extends DiagnosticEmitter { expr = Node.createCommaExpression(commaExprs, tn.range(startPos, tn.pos)); break; } - default: { + // Optional chaining + case Token.QUESTION_DOT: { - // PropertyAccessExpression - if (token == Token.DOT) { - if (tn.skipIdentifier()) { - next = Node.createIdentifierExpression(tn.readIdentifier(), tn.range()); - } else { - next = this.parseExpression(tn, - isRightAssociative(token) - ? nextPrecedence - : nextPrecedence + 1 - ); - if (!next) return null; - } - if (next.kind == NodeKind.IDENTIFIER) { // expr '.' Identifier - expr = Node.createPropertyAccessExpression( - expr, - next, - tn.range(startPos, tn.pos) - ); - } else if (next.kind == NodeKind.CALL) { // expr '.' CallExpression - expr = this.joinPropertyCall(tn, startPos, expr, next); - if (!expr) return null; - } else { + // ElementAccessExpression + if (tn.skip(Token.OPENBRACKET)) { + next = this.parseExpression(tn); // reports + if (!next) return null; + if (!tn.skip(Token.CLOSEBRACKET)) { this.error( - DiagnosticCode.Identifier_expected, - next.range + DiagnosticCode._0_expected, + tn.range(), "]" ); return null; } + expr = Node.createElementAccessExpression( + expr, + next, + true, + tn.range(startPos, tn.pos) + ); + break; - // BinaryExpression + // CallExpression? + } else { + let typeArguments: TypeNode[] | null = null; + if ( + tn.skip(Token.OPENPAREN) + || + nodeIsGenericCallable(expr.kind) && (typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn)) !== null + ) { + let args = this.parseArguments(tn); + if (!args) return null; + expr = Node.createCallExpression( + expr, + typeArguments, + args, + true, + tn.range(expr.range.start, tn.pos) + ); + break; + } + } + // otherwise fall-through + isOptionalChaining = true; + } + // PropertyAccessExpression + case Token.DOT: { + if (tn.skipIdentifier()) { + next = Node.createIdentifierExpression(tn.readIdentifier(), tn.range()); } else { next = this.parseExpression(tn, isRightAssociative(token) @@ -3796,8 +3827,35 @@ export class Parser extends DiagnosticEmitter { : nextPrecedence + 1 ); if (!next) return null; - expr = Node.createBinaryExpression(token, expr, next, tn.range(startPos, tn.pos)); } + if (next.kind == NodeKind.IDENTIFIER) { // expr '.' Identifier + expr = Node.createPropertyAccessExpression( + expr, + next, + isOptionalChaining, + tn.range(startPos, tn.pos) + ); + } else if (next.kind == NodeKind.CALL) { // expr '.' CallExpression + expr = this.joinPropertyCall(tn, startPos, expr, next, isOptionalChaining); + if (!expr) return null; + } else { + this.error( + DiagnosticCode.Identifier_expected, + next.range + ); + return null; + } + break; + } + // BinaryExpression + default: { + next = this.parseExpression(tn, + isRightAssociative(token) + ? nextPrecedence + : nextPrecedence + 1 + ); + if (!next) return null; + expr = Node.createBinaryExpression(token, expr, next, tn.range(startPos, tn.pos)); break; } } @@ -3810,20 +3868,24 @@ export class Parser extends DiagnosticEmitter { tn: Tokenizer, startPos: i32, expr: Expression, - call: CallExpression + call: CallExpression, + isOptionalChaining: bool ): Expression | null { var callee = call.expression; switch (callee.kind) { case NodeKind.IDENTIFIER: { // join property access and use as call target + // -> [ Identifier '.' foo ]() call.expression = Node.createPropertyAccessExpression( expr, callee, + isOptionalChaining, tn.range(startPos, tn.pos) ); break; } case NodeKind.CALL: { // join call target und wrap the original call around it - let inner = this.joinPropertyCall(tn, startPos, expr, callee); + // -> [ foo() '.' bar ]() + let inner = this.joinPropertyCall(tn, startPos, expr, callee, isOptionalChaining); if (!inner) return null; call.expression = inner; call.range = tn.range(startPos, tn.pos); @@ -3857,6 +3919,7 @@ export class Parser extends DiagnosticEmitter { expr, typeArguments, args, + false, tn.range(expr.range.start, tn.pos) ); } @@ -4010,7 +4073,8 @@ function determinePrecedence(kind: Token): Precedence { case Token.AMPERSAND_EQUALS: case Token.CARET_EQUALS: case Token.BAR_EQUALS: return Precedence.ASSIGNMENT; - case Token.QUESTION: return Precedence.CONDITIONAL; + case Token.QUESTION: + case Token.QUESTION_QUESTION: return Precedence.CONDITIONAL; case Token.BAR_BAR: return Precedence.LOGICAL_OR; case Token.AMPERSAND_AMPERSAND: return Precedence.LOGICAL_AND; case Token.BAR: return Precedence.BITWISE_OR; @@ -4041,7 +4105,8 @@ function determinePrecedence(kind: Token): Precedence { case Token.DOT: case Token.NEW: case Token.OPENBRACKET: - case Token.EXCLAMATION: return Precedence.MEMBERACCESS; + case Token.EXCLAMATION: + case Token.QUESTION_DOT: return Precedence.MEMBERACCESS; } return Precedence.NONE; } diff --git a/src/program.ts b/src/program.ts index 3bdd75a847..53fffbcd3d 100644 --- a/src/program.ts +++ b/src/program.ts @@ -636,6 +636,7 @@ export class Program extends DiagnosticEmitter { this.registerNativeType(CommonSymbols.void_, Type.void); this.registerNativeType(CommonSymbols.number, Type.f64); // alias this.registerNativeType(CommonSymbols.boolean, Type.bool); // alias + this.registerNativeType(CommonSymbols.auto, Type.auto); // internal this.nativeFile.add(CommonSymbols.native, new TypeDefinition( CommonSymbols.native, this.nativeFile, @@ -1374,7 +1375,7 @@ export class Program extends DiagnosticEmitter { ): void { if (decorators) { for (let i = 0, k = decorators.length; i < k; ++i) { - let decorator = decorators[i]; + let decorator: DecoratorNode = decorators[i]; // FIXME: Why does TS 3.7 complain here? switch (decorator.decoratorKind) { case DecoratorKind.OPERATOR: case DecoratorKind.OPERATOR_BINARY: diff --git a/src/tokenizer.ts b/src/tokenizer.ts index 23e4ad95f7..f6afbf36cc 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -137,6 +137,8 @@ export enum Token { AMPERSAND_AMPERSAND, BAR_BAR, QUESTION, + QUESTION_QUESTION, + QUESTION_DOT, COLON, EQUALS, PLUS_EQUALS, @@ -413,6 +415,7 @@ export function operatorTokenToString(token: Token): string { case Token.TILDE: return "~"; case Token.AMPERSAND_AMPERSAND: return "&&"; case Token.BAR_BAR: return "||"; + case Token.QUESTION_QUESTION: return "??"; case Token.EQUALS: return "="; case Token.PLUS_EQUALS: return "+="; case Token.MINUS_EQUALS: return "-="; @@ -878,6 +881,21 @@ export class Tokenizer extends DiagnosticEmitter { } case CharCode.QUESTION: { ++this.pos; + if (maxTokenLength > 1 && this.pos < end) { + let chr = text.charCodeAt(this.pos); + if (chr == CharCode.QUESTION) { + ++this.pos; + return Token.QUESTION_QUESTION; + } else if ( + chr == CharCode.DOT && ( + this.pos + 1 == end || + !isDecimalDigit(text.charCodeAt(this.pos + 1)) + ) + ) { + ++this.pos; + return Token.QUESTION_DOT; + } + } return Token.QUESTION; } case CharCode.OPENBRACKET: { diff --git a/std/assembly/builtins.ts b/std/assembly/builtins.ts index d1aa36a735..84e8639e1f 100644 --- a/std/assembly/builtins.ts +++ b/std/assembly/builtins.ts @@ -124,7 +124,7 @@ export declare function load(offset: usize, immOffset?: usize, immAlign?: usi // @ts-ignore: decorator @unsafe @builtin -export declare function store(offset: usize, value: void, immOffset?: usize, immAlign?: usize): void; +export declare function store(offset: usize, value: auto, immOffset?: usize, immAlign?: usize): void; // @ts-ignore: decorator @builtin @@ -152,11 +152,11 @@ export declare function select(ifTrue: T, ifFalse: T, condition: bool): T; // @ts-ignore: decorator @unsafe @builtin -export declare function unreachable(): void; +export declare function unreachable(): auto; // @ts-ignore: decorator @builtin -export declare function changetype(value: void): T; +export declare function changetype(value: auto): T; // @ts-ignore: decorator @builtin @@ -168,15 +168,15 @@ export declare function unchecked(expr: T): T; // @ts-ignore: decorator @unsafe @builtin -export declare function call_indirect(target: void, ...args: void[]): T; +export declare function call_indirect(target: auto, ...args: auto[]): T; // @ts-ignore: decorator @unsafe @builtin -export declare function call_direct(target: void, ...args: void[]): T; +export declare function call_direct(target: auto, ...args: auto[]): T; // @ts-ignore: decorator @builtin -export declare function instantiate(...args: void[]): T; +export declare function instantiate(...args: auto[]): T; export namespace atomic { // @ts-ignore: decorator @@ -238,7 +238,7 @@ export const enum AtomicWaitResult { // @ts-ignore: decorator @builtin -export declare function i8(value: void): i8; +export declare function i8(value: auto): i8; export namespace i8 { @@ -253,7 +253,7 @@ export namespace i8 { // @ts-ignore: decorator @builtin -export declare function i16(value: void): i16; +export declare function i16(value: auto): i16; export namespace i16 { @@ -268,7 +268,7 @@ export namespace i16 { // @ts-ignore: decorator @builtin -export declare function i32(value: void): i32; +export declare function i32(value: auto): i32; export namespace i32 { @@ -463,7 +463,7 @@ export namespace i32 { // @ts-ignore: decorator @builtin -export declare function i64(value: void): i64; +export declare function i64(value: auto): i64; export namespace i64 { @@ -709,7 +709,7 @@ export namespace i64 { // @ts-ignore: decorator @builtin -export declare function isize(value: void): isize; +export declare function isize(value: auto): isize; export namespace isize { @@ -728,7 +728,7 @@ export namespace isize { // @ts-ignore: decorator @builtin -export declare function u8(value: void): u8; +export declare function u8(value: auto): u8; export namespace u8 { @@ -743,7 +743,7 @@ export namespace u8 { // @ts-ignore: decorator @builtin -export declare function u16(value: void): u16; +export declare function u16(value: auto): u16; export namespace u16 { @@ -758,7 +758,7 @@ export namespace u16 { // @ts-ignore: decorator @builtin -export declare function u32(value: void): u32; +export declare function u32(value: auto): u32; export namespace u32 { @@ -773,7 +773,7 @@ export namespace u32 { // @ts-ignore: decorator @builtin -export declare function u64(value: void): u64; +export declare function u64(value: auto): u64; export namespace u64 { @@ -788,7 +788,7 @@ export namespace u64 { // @ts-ignore: decorator @builtin -export declare function usize(value: void): usize; +export declare function usize(value: auto): usize; export namespace usize { @@ -805,7 +805,7 @@ export namespace usize { // @ts-ignore: decorator @builtin -export declare function bool(value: void): bool; +export declare function bool(value: auto): bool; export namespace bool { @@ -820,7 +820,7 @@ export namespace bool { // @ts-ignore: decorator @builtin -export declare function f32(value: void): f32; +export declare function f32(value: auto): f32; export namespace f32 { @@ -899,7 +899,7 @@ export namespace f32 { // @ts-ignore: decorator @builtin -export declare function f64(value: void): f64; +export declare function f64(value: auto): f64; export namespace f64 { diff --git a/std/assembly/index.d.ts b/std/assembly/index.d.ts index cd83a7c7af..6710e25ce9 100644 --- a/std/assembly/index.d.ts +++ b/std/assembly/index.d.ts @@ -37,6 +37,8 @@ declare type f64 = number; declare type v128 = object; /** A host reference. */ declare type anyref = object; +/** Special internal type used with builtins. Do not use. */ +declare type auto = boolean | number | object; // Compiler hints diff --git a/tests/parser/nullish-coalescing.ts b/tests/parser/nullish-coalescing.ts new file mode 100644 index 0000000000..c92080163f --- /dev/null +++ b/tests/parser/nullish-coalescing.ts @@ -0,0 +1 @@ +a = b ?? c; diff --git a/tests/parser/nullish-coalescing.ts.fixture.ts b/tests/parser/nullish-coalescing.ts.fixture.ts new file mode 100644 index 0000000000..c92080163f --- /dev/null +++ b/tests/parser/nullish-coalescing.ts.fixture.ts @@ -0,0 +1 @@ +a = b ?? c; diff --git a/tests/parser/optional-chaining.ts b/tests/parser/optional-chaining.ts new file mode 100644 index 0000000000..91c3623b06 --- /dev/null +++ b/tests/parser/optional-chaining.ts @@ -0,0 +1,12 @@ +a?.b; +a?.b.c; +a?.b?.c; +(a + b)?.c; +a + b?.c; + +a?.[0]; + +a?.(b); +a?.(b); + +a.b?.(c); diff --git a/tests/parser/optional-chaining.ts.fixture.ts b/tests/parser/optional-chaining.ts.fixture.ts new file mode 100644 index 0000000000..bcd57c5798 --- /dev/null +++ b/tests/parser/optional-chaining.ts.fixture.ts @@ -0,0 +1,9 @@ +a?.b; +a?.b.c; +a?.b?.c; +(a + b)?.c; +a + b?.c; +a?.[0]; +a?.(b); +a?.(b); +a.b?.(c);