@@ -2,14 +2,10 @@ import * as ts from "typescript";
22import * as lua from "../../../LuaAST" ;
33import { FunctionVisitor , TransformationContext } from "../../context" ;
44import { AnnotationKind , getTypeAnnotations } from "../../utils/annotations" ;
5- import {
6- extensionInvalidInstanceOf ,
7- luaTableInvalidInstanceOf ,
8- unsupportedNullishCoalescing ,
9- } from "../../utils/diagnostics" ;
5+ import { extensionInvalidInstanceOf , luaTableInvalidInstanceOf } from "../../utils/diagnostics" ;
106import { createImmediatelyInvokedFunctionExpression , wrapInToStringForConcat } from "../../utils/lua-ast" ;
117import { LuaLibFeature , transformLuaLibFunction } from "../../utils/lualib" ;
12- import { isStandardLibraryType , isStringType } from "../../utils/typescript" ;
8+ import { isStandardLibraryType , isStringType , typeCanSatisfy } from "../../utils/typescript" ;
139import { transformTypeOfBinaryExpression } from "../typeof" ;
1410import { transformAssignmentExpression , transformAssignmentStatement } from "./assignments" ;
1511import { BitOperator , isBitOperator , transformBinaryBitOperation } from "./bit" ;
@@ -137,13 +133,7 @@ export const transformBinaryExpression: FunctionVisitor<ts.BinaryExpression> = (
137133 }
138134
139135 case ts . SyntaxKind . QuestionQuestionToken : {
140- context . diagnostics . push ( unsupportedNullishCoalescing ( node . operatorToken ) ) ;
141- return lua . createBinaryExpression (
142- context . transformExpression ( node . left ) ,
143- context . transformExpression ( node . right ) ,
144- lua . SyntaxKind . OrOperator ,
145- node
146- ) ;
136+ return transformNullishCoalescingExpression ( context , node ) ;
147137 }
148138
149139 default :
@@ -185,3 +175,42 @@ export function transformBinaryExpressionStatement(
185175 return lua . createDoStatement ( statements , expression ) ;
186176 }
187177}
178+
179+ function transformNullishCoalescingExpression (
180+ context : TransformationContext ,
181+ node : ts . BinaryExpression
182+ ) : lua . Expression {
183+ const lhsType = context . checker . getTypeAtLocation ( node . left ) ;
184+
185+ // Check if we can take a shortcut to 'lhs or rhs' if the left-hand side cannot be 'false'.
186+ const typeCanBeFalse = ( type : ts . Type ) =>
187+ ( type . flags & ( ts . TypeFlags . Any | ts . TypeFlags . Unknown | ts . TypeFlags . Boolean ) ) !== 0 ||
188+ ( type . flags & ts . TypeFlags . BooleanLiteral & ts . TypeFlags . PossiblyFalsy ) !== 0 ;
189+ if ( typeCanSatisfy ( context , lhsType , typeCanBeFalse ) ) {
190+ // lhs can be false, transform to IIFE
191+ const lhsIdentifier = lua . createIdentifier ( "____lhs" ) ;
192+ const nilComparison = lua . createBinaryExpression (
193+ lua . cloneIdentifier ( lhsIdentifier ) ,
194+ lua . createNilLiteral ( ) ,
195+ lua . SyntaxKind . EqualityOperator
196+ ) ;
197+ // if ____ == nil then return rhs else return ____ end
198+ const ifStatement = lua . createIfStatement (
199+ nilComparison ,
200+ lua . createBlock ( [ lua . createReturnStatement ( [ context . transformExpression ( node . right ) ] ) ] ) ,
201+ lua . createBlock ( [ lua . createReturnStatement ( [ lua . cloneIdentifier ( lhsIdentifier ) ] ) ] )
202+ ) ;
203+ // (function(lhs') if lhs' == nil then return rhs else return lhs' end)(lhs)
204+ return lua . createCallExpression ( lua . createFunctionExpression ( lua . createBlock ( [ ifStatement ] ) , [ lhsIdentifier ] ) , [
205+ context . transformExpression ( node . left ) ,
206+ ] ) ;
207+ } else {
208+ // lhs or rhs
209+ return lua . createBinaryExpression (
210+ context . transformExpression ( node . left ) ,
211+ context . transformExpression ( node . right ) ,
212+ lua . SyntaxKind . OrOperator ,
213+ node
214+ ) ;
215+ }
216+ }
0 commit comments