Skip to content

Commit 52b8b92

Browse files
committed
fix: allow using nullable strings in template literals
We basically compile nullable strings down to a ternary: `expr ? expr.toString() : "null"` Fixes #2918.
1 parent 44b658d commit 52b8b92

File tree

4 files changed

+350
-104
lines changed

4 files changed

+350
-104
lines changed

src/compiler.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10145,6 +10145,7 @@ export class Compiler extends DiagnosticEmitter {
1014510145

1014610146
/** Makes a string conversion of the given expression. */
1014710147
makeToString(expr: ExpressionRef, type: Type, reportNode: Node): ExpressionRef {
10148+
let module = this.module;
1014810149
let stringType = this.program.stringInstance.type;
1014910150
if (type == stringType) {
1015010151
return expr;
@@ -10161,15 +10162,30 @@ export class Compiler extends DiagnosticEmitter {
1016110162
reportNode
1016210163
)) {
1016310164
this.currentType = stringType;
10164-
return this.module.unreachable();
10165+
return module.unreachable();
1016510166
}
1016610167
if (!type.isStrictlyAssignableTo(assert(toStringSignature.thisType))) {
10167-
this.errorRelated(
10168-
DiagnosticCode.The_this_types_of_each_signature_are_incompatible,
10169-
reportNode.range, toStringInstance.identifierAndSignatureRange
10168+
if (!type.is(TypeFlags.Nullable)) {
10169+
this.errorRelated(
10170+
DiagnosticCode.The_this_types_of_each_signature_are_incompatible,
10171+
reportNode.range, toStringInstance.identifierAndSignatureRange
10172+
);
10173+
this.currentType = stringType;
10174+
return module.unreachable();
10175+
}
10176+
10177+
// Attempt to retry on the non-nullable form of the type, wrapped in a ternary:
10178+
// `expr ? expr.toString() : "null"`
10179+
const tempLocal = this.currentFlow.getTempLocal(type);
10180+
return module.if(
10181+
module.local_tee(tempLocal.index, expr, type.isManaged),
10182+
this.makeToString(
10183+
module.local_get(tempLocal.index, type.toRef()),
10184+
type.nonNullableType,
10185+
reportNode
10186+
),
10187+
this.ensureStaticString("null")
1017010188
);
10171-
this.currentType = stringType;
10172-
return this.module.unreachable();
1017310189
}
1017410190
let toStringReturnType = toStringSignature.returnType;
1017510191
if (!toStringReturnType.isStrictlyAssignableTo(stringType)) {
@@ -10178,7 +10194,7 @@ export class Compiler extends DiagnosticEmitter {
1017810194
reportNode.range, toStringInstance.identifierAndSignatureRange, toStringReturnType.toString(), stringType.toString()
1017910195
);
1018010196
this.currentType = stringType;
10181-
return this.module.unreachable();
10197+
return module.unreachable();
1018210198
}
1018310199
return this.makeCallDirect(toStringInstance, [ expr ], reportNode);
1018410200
}
@@ -10188,7 +10204,7 @@ export class Compiler extends DiagnosticEmitter {
1018810204
reportNode.range, type.toString(), stringType.toString()
1018910205
);
1019010206
this.currentType = stringType;
10191-
return this.module.unreachable();
10207+
return module.unreachable();
1019210208
}
1019310209

1019410210
/** Makes an allocation suitable to hold the data of an instance of the given class. */

tests/compiler/templateliteral.debug.wat

Lines changed: 143 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@
3939
(global $~lib/util/number/_K (mut i32) (i32.const 0))
4040
(global $~lib/util/number/_frc_pow (mut i64) (i64.const 0))
4141
(global $~lib/util/number/_exp_pow (mut i32) (i32.const 0))
42-
(global $~lib/rt/__rtti_base i32 (i32.const 4640))
43-
(global $~lib/memory/__data_end i32 (i32.const 4672))
44-
(global $~lib/memory/__stack_pointer (mut i32) (i32.const 37440))
45-
(global $~lib/memory/__heap_base i32 (i32.const 37440))
42+
(global $~lib/rt/__rtti_base i32 (i32.const 4848))
43+
(global $~lib/memory/__data_end i32 (i32.const 4880))
44+
(global $~lib/memory/__stack_pointer (mut i32) (i32.const 37648))
45+
(global $~lib/memory/__heap_base i32 (i32.const 37648))
4646
(global $~started (mut i32) (i32.const 0))
4747
(memory $0 1)
4848
(data $0 (i32.const 12) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\02\00\00\00a\00\00\00\00\00\00\00\00\00\00\00")
@@ -98,11 +98,16 @@
9898
(data $50 (i32.const 4348) ",\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\14\00\00\00r\00e\00f\00#\001\00r\00e\00f\00#\002\00\00\00\00\00\00\00\00\00")
9999
(data $51 (i32.const 4396) ",\00\00\00\03\00\00\00\00\00\00\00\04\00\00\00\14\00\00\00p\02\00\00\00\00\00\00\90\02\00\00\00\00\00\00\b0\02\00\00\00\00\00\00\00\00\00\00")
100100
(data $52 (i32.const 4444) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00$\00\00\00(\00A\00=\00r\00e\00f\00#\001\00,\00 \00B\00=\00r\00e\00f\00#\002\00)\00\00\00\00\00\00\00\00\00")
101-
(data $53 (i32.const 4508) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\02\00\00\00c\00\00\00\00\00\00\00\00\00\00\00")
102-
(data $54 (i32.const 4540) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\02\00\00\00:\00\00\00\00\00\00\00\00\00\00\00")
103-
(data $55 (i32.const 4572) "\1c\00\00\00\03\00\00\00\00\00\00\00\04\00\00\00\0c\00\00\00\00\00\00\00\d0\11\00\00\00\00\00\00")
104-
(data $56 (i32.const 4604) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\n\00\00\00a\00:\00b\00:\00c\00\00\00")
105-
(data $57 (i32.const 4640) "\07\00\00\00 \00\00\00 \00\00\00 \00\00\00\00\00\00\00\04A\00\00 \00\00\00\00\00\00\00")
101+
(data $53 (i32.const 4508) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\06\00\00\00c\00:\00 \00\00\00\00\00\00\00")
102+
(data $54 (i32.const 4540) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\n\00\00\00;\00 \00d\00:\00 \00\00\00")
103+
(data $55 (i32.const 4572) ",\00\00\00\03\00\00\00\00\00\00\00\04\00\00\00\10\00\00\00\b0\11\00\00\00\00\00\00\d0\11\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
104+
(data $56 (i32.const 4620) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\08\00\00\00n\00u\00l\00l\00\00\00\00\00")
105+
(data $57 (i32.const 4652) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\"\00\00\00c\00:\00 \00r\00e\00f\00#\003\00;\00 \00d\00:\00 \00n\00u\00l\00l\00\00\00\00\00\00\00\00\00\00\00")
106+
(data $58 (i32.const 4716) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\02\00\00\00c\00\00\00\00\00\00\00\00\00\00\00")
107+
(data $59 (i32.const 4748) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\02\00\00\00:\00\00\00\00\00\00\00\00\00\00\00")
108+
(data $60 (i32.const 4780) "\1c\00\00\00\03\00\00\00\00\00\00\00\04\00\00\00\0c\00\00\00\00\00\00\00\a0\12\00\00\00\00\00\00")
109+
(data $61 (i32.const 4812) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\n\00\00\00a\00:\00b\00:\00c\00\00\00")
110+
(data $62 (i32.const 4848) "\07\00\00\00 \00\00\00 \00\00\00 \00\00\00\00\00\00\00\04A\00\00 \00\00\00\00\00\00\00")
106111
(table $0 1 1 funcref)
107112
(elem $0 (i32.const 1))
108113
(export "memory" (memory $0))
@@ -4365,6 +4370,7 @@
43654370
call $templateliteral/test_float
43664371
call $templateliteral/test_fast_paths_string
43674372
call $templateliteral/test_ref
4373+
call $templateliteral/test_null
43684374
call $templateliteral/test_recursive
43694375
)
43704376
(func $~lib/rt/__visit_globals (param $0 i32)
@@ -4519,8 +4525,8 @@
45194525
global.get $~lib/memory/__data_end
45204526
i32.lt_s
45214527
if
4522-
i32.const 37472
4523-
i32.const 37520
4528+
i32.const 37680
4529+
i32.const 37728
45244530
i32.const 1
45254531
i32.const 1
45264532
call $~lib/builtins/abort
@@ -5991,6 +5997,126 @@
59915997
i32.add
59925998
global.set $~lib/memory/__stack_pointer
59935999
)
6000+
(func $templateliteral/test_null
6001+
(local $c i32)
6002+
(local $d i32)
6003+
(local $2 i32)
6004+
(local $3 i32)
6005+
(local $4 i32)
6006+
(local $5 i32)
6007+
global.get $~lib/memory/__stack_pointer
6008+
i32.const 32
6009+
i32.sub
6010+
global.set $~lib/memory/__stack_pointer
6011+
call $~stack_check
6012+
global.get $~lib/memory/__stack_pointer
6013+
i32.const 0
6014+
i32.const 32
6015+
memory.fill
6016+
global.get $~lib/memory/__stack_pointer
6017+
i32.const 0
6018+
i32.const 3
6019+
call $templateliteral/Ref#constructor
6020+
local.tee $c
6021+
i32.store
6022+
i32.const 0
6023+
local.set $d
6024+
global.get $~lib/memory/__stack_pointer
6025+
local.get $c
6026+
local.set $5
6027+
global.get $~lib/memory/__stack_pointer
6028+
local.get $5
6029+
i32.store offset=12
6030+
local.get $5
6031+
call $templateliteral/Ref#toString
6032+
local.tee $2
6033+
i32.store offset=16
6034+
global.get $~lib/memory/__stack_pointer
6035+
global.get $~lib/memory/__stack_pointer
6036+
local.get $d
6037+
local.tee $4
6038+
i32.store offset=20
6039+
local.get $4
6040+
if (result i32)
6041+
local.get $4
6042+
local.set $5
6043+
global.get $~lib/memory/__stack_pointer
6044+
local.get $5
6045+
i32.store offset=12
6046+
local.get $5
6047+
call $templateliteral/Ref#toString
6048+
else
6049+
i32.const 4640
6050+
end
6051+
local.tee $3
6052+
i32.store offset=24
6053+
i32.const 4592
6054+
local.set $5
6055+
global.get $~lib/memory/__stack_pointer
6056+
local.get $5
6057+
i32.store offset=12
6058+
local.get $5
6059+
i32.const 1
6060+
local.get $2
6061+
local.set $5
6062+
global.get $~lib/memory/__stack_pointer
6063+
local.get $5
6064+
i32.store offset=28
6065+
local.get $5
6066+
call $~lib/staticarray/StaticArray<~lib/string/String>#__uset
6067+
i32.const 4592
6068+
local.set $5
6069+
global.get $~lib/memory/__stack_pointer
6070+
local.get $5
6071+
i32.store offset=12
6072+
local.get $5
6073+
i32.const 3
6074+
local.get $3
6075+
local.set $5
6076+
global.get $~lib/memory/__stack_pointer
6077+
local.get $5
6078+
i32.store offset=28
6079+
local.get $5
6080+
call $~lib/staticarray/StaticArray<~lib/string/String>#__uset
6081+
i32.const 4592
6082+
local.set $5
6083+
global.get $~lib/memory/__stack_pointer
6084+
local.get $5
6085+
i32.store offset=12
6086+
local.get $5
6087+
i32.const 160
6088+
local.set $5
6089+
global.get $~lib/memory/__stack_pointer
6090+
local.get $5
6091+
i32.store offset=28
6092+
local.get $5
6093+
call $~lib/staticarray/StaticArray<~lib/string/String>#join
6094+
local.set $5
6095+
global.get $~lib/memory/__stack_pointer
6096+
local.get $5
6097+
i32.store offset=4
6098+
local.get $5
6099+
i32.const 4672
6100+
local.set $5
6101+
global.get $~lib/memory/__stack_pointer
6102+
local.get $5
6103+
i32.store offset=8
6104+
local.get $5
6105+
call $~lib/string/String.__eq
6106+
i32.eqz
6107+
if
6108+
i32.const 0
6109+
i32.const 96
6110+
i32.const 60
6111+
i32.const 3
6112+
call $~lib/builtins/abort
6113+
unreachable
6114+
end
6115+
global.get $~lib/memory/__stack_pointer
6116+
i32.const 32
6117+
i32.add
6118+
global.set $~lib/memory/__stack_pointer
6119+
)
59946120
(func $templateliteral/RecursiveObject#constructor (param $this i32) (param $key i32) (param $val i32) (result i32)
59956121
(local $3 i32)
59966122
global.get $~lib/memory/__stack_pointer
@@ -6110,7 +6236,7 @@
61106236
call $templateliteral/RecursiveObject#toString
61116237
local.tee $3
61126238
i32.store offset=12
6113-
i32.const 4592
6239+
i32.const 4800
61146240
local.set $4
61156241
global.get $~lib/memory/__stack_pointer
61166242
local.get $4
@@ -6124,7 +6250,7 @@
61246250
i32.store offset=16
61256251
local.get $4
61266252
call $~lib/staticarray/StaticArray<~lib/string/String>#__uset
6127-
i32.const 4592
6253+
i32.const 4800
61286254
local.set $4
61296255
global.get $~lib/memory/__stack_pointer
61306256
local.get $4
@@ -6138,7 +6264,7 @@
61386264
i32.store offset=16
61396265
local.get $4
61406266
call $~lib/staticarray/StaticArray<~lib/string/String>#__uset
6141-
i32.const 4592
6267+
i32.const 4800
61426268
local.set $4
61436269
global.get $~lib/memory/__stack_pointer
61446270
local.get $4
@@ -6175,7 +6301,7 @@
61756301
memory.fill
61766302
global.get $~lib/memory/__stack_pointer
61776303
i32.const 0
6178-
i32.const 4528
6304+
i32.const 4736
61796305
local.set $3
61806306
global.get $~lib/memory/__stack_pointer
61816307
local.get $3
@@ -6231,7 +6357,7 @@
62316357
local.get $3
62326358
i32.store
62336359
local.get $3
6234-
i32.const 4624
6360+
i32.const 4832
62356361
local.set $3
62366362
global.get $~lib/memory/__stack_pointer
62376363
local.get $3
@@ -6242,7 +6368,7 @@
62426368
if
62436369
i32.const 0
62446370
i32.const 96
6245-
i32.const 118
6371+
i32.const 125
62466372
i32.const 3
62476373
call $~lib/builtins/abort
62486374
unreachable

0 commit comments

Comments
 (0)