Skip to content

Commit 6bac413

Browse files
WasmGen: Add "conversion" category
1 parent bef9c4a commit 6bac413

File tree

6 files changed

+222
-494
lines changed

6 files changed

+222
-494
lines changed

Sources/WAT/InstructionEncoder.swift

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -295,31 +295,46 @@ extension InstructionEncoder {
295295

296296
try encodeInstruction(opcode, prefix)
297297
}
298-
mutating func visitI32WrapI64() throws { try encodeInstruction(0xA7, nil) }
299-
mutating func visitI32TruncF32S() throws { try encodeInstruction(0xA8, nil) }
300-
mutating func visitI32TruncF32U() throws { try encodeInstruction(0xA9, nil) }
301-
mutating func visitI32TruncF64S() throws { try encodeInstruction(0xAA, nil) }
302-
mutating func visitI32TruncF64U() throws { try encodeInstruction(0xAB, nil) }
303-
mutating func visitI64ExtendI32S() throws { try encodeInstruction(0xAC, nil) }
304-
mutating func visitI64ExtendI32U() throws { try encodeInstruction(0xAD, nil) }
305-
mutating func visitI64TruncF32S() throws { try encodeInstruction(0xAE, nil) }
306-
mutating func visitI64TruncF32U() throws { try encodeInstruction(0xAF, nil) }
307-
mutating func visitI64TruncF64S() throws { try encodeInstruction(0xB0, nil) }
308-
mutating func visitI64TruncF64U() throws { try encodeInstruction(0xB1, nil) }
309-
mutating func visitF32ConvertI32S() throws { try encodeInstruction(0xB2, nil) }
310-
mutating func visitF32ConvertI32U() throws { try encodeInstruction(0xB3, nil) }
311-
mutating func visitF32ConvertI64S() throws { try encodeInstruction(0xB4, nil) }
312-
mutating func visitF32ConvertI64U() throws { try encodeInstruction(0xB5, nil) }
313-
mutating func visitF32DemoteF64() throws { try encodeInstruction(0xB6, nil) }
314-
mutating func visitF64ConvertI32S() throws { try encodeInstruction(0xB7, nil) }
315-
mutating func visitF64ConvertI32U() throws { try encodeInstruction(0xB8, nil) }
316-
mutating func visitF64ConvertI64S() throws { try encodeInstruction(0xB9, nil) }
317-
mutating func visitF64ConvertI64U() throws { try encodeInstruction(0xBA, nil) }
318-
mutating func visitF64PromoteF32() throws { try encodeInstruction(0xBB, nil) }
319-
mutating func visitI32ReinterpretF32() throws { try encodeInstruction(0xBC, nil) }
320-
mutating func visitI64ReinterpretF64() throws { try encodeInstruction(0xBD, nil) }
321-
mutating func visitF32ReinterpretI32() throws { try encodeInstruction(0xBE, nil) }
322-
mutating func visitF64ReinterpretI64() throws { try encodeInstruction(0xBF, nil) }
298+
mutating func visitConversion(_ conversion: Instruction.Conversion) throws {
299+
let (prefix, opcode): (UInt8?, UInt8)
300+
switch conversion {
301+
case .i32WrapI64: (prefix, opcode) = (nil, 0xA7)
302+
case .i32TruncF32S: (prefix, opcode) = (nil, 0xA8)
303+
case .i32TruncF32U: (prefix, opcode) = (nil, 0xA9)
304+
case .i32TruncF64S: (prefix, opcode) = (nil, 0xAA)
305+
case .i32TruncF64U: (prefix, opcode) = (nil, 0xAB)
306+
case .i64ExtendI32S: (prefix, opcode) = (nil, 0xAC)
307+
case .i64ExtendI32U: (prefix, opcode) = (nil, 0xAD)
308+
case .i64TruncF32S: (prefix, opcode) = (nil, 0xAE)
309+
case .i64TruncF32U: (prefix, opcode) = (nil, 0xAF)
310+
case .i64TruncF64S: (prefix, opcode) = (nil, 0xB0)
311+
case .i64TruncF64U: (prefix, opcode) = (nil, 0xB1)
312+
case .f32ConvertI32S: (prefix, opcode) = (nil, 0xB2)
313+
case .f32ConvertI32U: (prefix, opcode) = (nil, 0xB3)
314+
case .f32ConvertI64S: (prefix, opcode) = (nil, 0xB4)
315+
case .f32ConvertI64U: (prefix, opcode) = (nil, 0xB5)
316+
case .f32DemoteF64: (prefix, opcode) = (nil, 0xB6)
317+
case .f64ConvertI32S: (prefix, opcode) = (nil, 0xB7)
318+
case .f64ConvertI32U: (prefix, opcode) = (nil, 0xB8)
319+
case .f64ConvertI64S: (prefix, opcode) = (nil, 0xB9)
320+
case .f64ConvertI64U: (prefix, opcode) = (nil, 0xBA)
321+
case .f64PromoteF32: (prefix, opcode) = (nil, 0xBB)
322+
case .i32ReinterpretF32: (prefix, opcode) = (nil, 0xBC)
323+
case .i64ReinterpretF64: (prefix, opcode) = (nil, 0xBD)
324+
case .f32ReinterpretI32: (prefix, opcode) = (nil, 0xBE)
325+
case .f64ReinterpretI64: (prefix, opcode) = (nil, 0xBF)
326+
case .i32TruncSatF32S: (prefix, opcode) = (0xFC, 0x00)
327+
case .i32TruncSatF32U: (prefix, opcode) = (0xFC, 0x01)
328+
case .i32TruncSatF64S: (prefix, opcode) = (0xFC, 0x02)
329+
case .i32TruncSatF64U: (prefix, opcode) = (0xFC, 0x03)
330+
case .i64TruncSatF32S: (prefix, opcode) = (0xFC, 0x04)
331+
case .i64TruncSatF32U: (prefix, opcode) = (0xFC, 0x05)
332+
case .i64TruncSatF64S: (prefix, opcode) = (0xFC, 0x06)
333+
case .i64TruncSatF64U: (prefix, opcode) = (0xFC, 0x07)
334+
}
335+
336+
try encodeInstruction(opcode, prefix)
337+
}
323338
mutating func visitMemoryInit(dataIndex: UInt32) throws {
324339
try encodeInstruction(0x08, 0xFC)
325340
try encodeImmediates(dataIndex: dataIndex)
@@ -368,12 +383,4 @@ extension InstructionEncoder {
368383
try encodeInstruction(0x10, 0xFC)
369384
try encodeImmediates(table: table)
370385
}
371-
mutating func visitI32TruncSatF32S() throws { try encodeInstruction(0x00, 0xFC) }
372-
mutating func visitI32TruncSatF32U() throws { try encodeInstruction(0x01, 0xFC) }
373-
mutating func visitI32TruncSatF64S() throws { try encodeInstruction(0x02, 0xFC) }
374-
mutating func visitI32TruncSatF64U() throws { try encodeInstruction(0x03, 0xFC) }
375-
mutating func visitI64TruncSatF32S() throws { try encodeInstruction(0x04, 0xFC) }
376-
mutating func visitI64TruncSatF32U() throws { try encodeInstruction(0x05, 0xFC) }
377-
mutating func visitI64TruncSatF64S() throws { try encodeInstruction(0x06, 0xFC) }
378-
mutating func visitI64TruncSatF64U() throws { try encodeInstruction(0x07, 0xFC) }
379386
}

Sources/WAT/ParseInstruction.swift

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -252,31 +252,31 @@ func parseTextInstruction<V: InstructionVisitor>(keyword: String, expressionPars
252252
case "f64.min": return { return try $0.visitBinary(.f64Min) }
253253
case "f64.max": return { return try $0.visitBinary(.f64Max) }
254254
case "f64.copysign": return { return try $0.visitBinary(.f64Copysign) }
255-
case "i32.wrap_i64": return { return try $0.visitI32WrapI64() }
256-
case "i32.trunc_f32_s": return { return try $0.visitI32TruncF32S() }
257-
case "i32.trunc_f32_u": return { return try $0.visitI32TruncF32U() }
258-
case "i32.trunc_f64_s": return { return try $0.visitI32TruncF64S() }
259-
case "i32.trunc_f64_u": return { return try $0.visitI32TruncF64U() }
260-
case "i64.extend_i32_s": return { return try $0.visitI64ExtendI32S() }
261-
case "i64.extend_i32_u": return { return try $0.visitI64ExtendI32U() }
262-
case "i64.trunc_f32_s": return { return try $0.visitI64TruncF32S() }
263-
case "i64.trunc_f32_u": return { return try $0.visitI64TruncF32U() }
264-
case "i64.trunc_f64_s": return { return try $0.visitI64TruncF64S() }
265-
case "i64.trunc_f64_u": return { return try $0.visitI64TruncF64U() }
266-
case "f32.convert_i32_s": return { return try $0.visitF32ConvertI32S() }
267-
case "f32.convert_i32_u": return { return try $0.visitF32ConvertI32U() }
268-
case "f32.convert_i64_s": return { return try $0.visitF32ConvertI64S() }
269-
case "f32.convert_i64_u": return { return try $0.visitF32ConvertI64U() }
270-
case "f32.demote_f64": return { return try $0.visitF32DemoteF64() }
271-
case "f64.convert_i32_s": return { return try $0.visitF64ConvertI32S() }
272-
case "f64.convert_i32_u": return { return try $0.visitF64ConvertI32U() }
273-
case "f64.convert_i64_s": return { return try $0.visitF64ConvertI64S() }
274-
case "f64.convert_i64_u": return { return try $0.visitF64ConvertI64U() }
275-
case "f64.promote_f32": return { return try $0.visitF64PromoteF32() }
276-
case "i32.reinterpret_f32": return { return try $0.visitI32ReinterpretF32() }
277-
case "i64.reinterpret_f64": return { return try $0.visitI64ReinterpretF64() }
278-
case "f32.reinterpret_i32": return { return try $0.visitF32ReinterpretI32() }
279-
case "f64.reinterpret_i64": return { return try $0.visitF64ReinterpretI64() }
255+
case "i32.wrap_i64": return { return try $0.visitConversion(.i32WrapI64) }
256+
case "i32.trunc_f32_s": return { return try $0.visitConversion(.i32TruncF32S) }
257+
case "i32.trunc_f32_u": return { return try $0.visitConversion(.i32TruncF32U) }
258+
case "i32.trunc_f64_s": return { return try $0.visitConversion(.i32TruncF64S) }
259+
case "i32.trunc_f64_u": return { return try $0.visitConversion(.i32TruncF64U) }
260+
case "i64.extend_i32_s": return { return try $0.visitConversion(.i64ExtendI32S) }
261+
case "i64.extend_i32_u": return { return try $0.visitConversion(.i64ExtendI32U) }
262+
case "i64.trunc_f32_s": return { return try $0.visitConversion(.i64TruncF32S) }
263+
case "i64.trunc_f32_u": return { return try $0.visitConversion(.i64TruncF32U) }
264+
case "i64.trunc_f64_s": return { return try $0.visitConversion(.i64TruncF64S) }
265+
case "i64.trunc_f64_u": return { return try $0.visitConversion(.i64TruncF64U) }
266+
case "f32.convert_i32_s": return { return try $0.visitConversion(.f32ConvertI32S) }
267+
case "f32.convert_i32_u": return { return try $0.visitConversion(.f32ConvertI32U) }
268+
case "f32.convert_i64_s": return { return try $0.visitConversion(.f32ConvertI64S) }
269+
case "f32.convert_i64_u": return { return try $0.visitConversion(.f32ConvertI64U) }
270+
case "f32.demote_f64": return { return try $0.visitConversion(.f32DemoteF64) }
271+
case "f64.convert_i32_s": return { return try $0.visitConversion(.f64ConvertI32S) }
272+
case "f64.convert_i32_u": return { return try $0.visitConversion(.f64ConvertI32U) }
273+
case "f64.convert_i64_s": return { return try $0.visitConversion(.f64ConvertI64S) }
274+
case "f64.convert_i64_u": return { return try $0.visitConversion(.f64ConvertI64U) }
275+
case "f64.promote_f32": return { return try $0.visitConversion(.f64PromoteF32) }
276+
case "i32.reinterpret_f32": return { return try $0.visitConversion(.i32ReinterpretF32) }
277+
case "i64.reinterpret_f64": return { return try $0.visitConversion(.i64ReinterpretF64) }
278+
case "f32.reinterpret_i32": return { return try $0.visitConversion(.f32ReinterpretI32) }
279+
case "f64.reinterpret_i64": return { return try $0.visitConversion(.f64ReinterpretI64) }
280280
case "i32.extend8_s": return { return try $0.visitUnary(.i32Extend8S) }
281281
case "i32.extend16_s": return { return try $0.visitUnary(.i32Extend16S) }
282282
case "i64.extend8_s": return { return try $0.visitUnary(.i64Extend8S) }
@@ -318,14 +318,14 @@ func parseTextInstruction<V: InstructionVisitor>(keyword: String, expressionPars
318318
case "table.size":
319319
let (table) = try expressionParser.visitTableSize(wat: &wat)
320320
return { return try $0.visitTableSize(table: table) }
321-
case "i32.trunc_sat_f32_s": return { return try $0.visitI32TruncSatF32S() }
322-
case "i32.trunc_sat_f32_u": return { return try $0.visitI32TruncSatF32U() }
323-
case "i32.trunc_sat_f64_s": return { return try $0.visitI32TruncSatF64S() }
324-
case "i32.trunc_sat_f64_u": return { return try $0.visitI32TruncSatF64U() }
325-
case "i64.trunc_sat_f32_s": return { return try $0.visitI64TruncSatF32S() }
326-
case "i64.trunc_sat_f32_u": return { return try $0.visitI64TruncSatF32U() }
327-
case "i64.trunc_sat_f64_s": return { return try $0.visitI64TruncSatF64S() }
328-
case "i64.trunc_sat_f64_u": return { return try $0.visitI64TruncSatF64U() }
321+
case "i32.trunc_sat_f32_s": return { return try $0.visitConversion(.i32TruncSatF32S) }
322+
case "i32.trunc_sat_f32_u": return { return try $0.visitConversion(.i32TruncSatF32U) }
323+
case "i32.trunc_sat_f64_s": return { return try $0.visitConversion(.i32TruncSatF64S) }
324+
case "i32.trunc_sat_f64_u": return { return try $0.visitConversion(.i32TruncSatF64U) }
325+
case "i64.trunc_sat_f32_s": return { return try $0.visitConversion(.i64TruncSatF32S) }
326+
case "i64.trunc_sat_f32_u": return { return try $0.visitConversion(.i64TruncSatF32U) }
327+
case "i64.trunc_sat_f64_s": return { return try $0.visitConversion(.i64TruncSatF64S) }
328+
case "i64.trunc_sat_f64_u": return { return try $0.visitConversion(.i64TruncSatF64U) }
329329
default: return nil
330330
}
331331
}

Sources/WasmKit/Translator.swift

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,31 +1983,45 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
19831983
}
19841984
try visitUnary(operand, instruction)
19851985
}
1986-
mutating func visitI32WrapI64() throws -> Output { try visitConversion(.i64, .i32, Instruction.i32WrapI64) }
1987-
mutating func visitI32TruncF32S() throws -> Output { try visitConversion(.f32, .i32, Instruction.i32TruncF32S) }
1988-
mutating func visitI32TruncF32U() throws -> Output { try visitConversion(.f32, .i32, Instruction.i32TruncF32U) }
1989-
mutating func visitI32TruncF64S() throws -> Output { try visitConversion(.f64, .i32, Instruction.i32TruncF64S) }
1990-
mutating func visitI32TruncF64U() throws -> Output { try visitConversion(.f64, .i32, Instruction.i32TruncF64U) }
1991-
mutating func visitI64ExtendI32S() throws -> Output { try visitConversion(.i32, .i64, Instruction.i64ExtendI32S) }
1992-
mutating func visitI64ExtendI32U() throws -> Output { try visitConversion(.i32, .i64, Instruction.i64ExtendI32U) }
1993-
mutating func visitI64TruncF32S() throws -> Output { try visitConversion(.f32, .i64, Instruction.i64TruncF32S) }
1994-
mutating func visitI64TruncF32U() throws -> Output { try visitConversion(.f32, .i64, Instruction.i64TruncF32U) }
1995-
mutating func visitI64TruncF64S() throws -> Output { try visitConversion(.f64, .i64, Instruction.i64TruncF64S) }
1996-
mutating func visitI64TruncF64U() throws -> Output { try visitConversion(.f64, .i64, Instruction.i64TruncF64U) }
1997-
mutating func visitF32ConvertI32S() throws -> Output { try visitConversion(.i32, .f32, Instruction.f32ConvertI32S) }
1998-
mutating func visitF32ConvertI32U() throws -> Output { try visitConversion(.i32, .f32, Instruction.f32ConvertI32U) }
1999-
mutating func visitF32ConvertI64S() throws -> Output { try visitConversion(.i64, .f32, Instruction.f32ConvertI64S) }
2000-
mutating func visitF32ConvertI64U() throws -> Output { try visitConversion(.i64, .f32, Instruction.f32ConvertI64U) }
2001-
mutating func visitF32DemoteF64() throws -> Output { try visitConversion(.f64, .f32, Instruction.f32DemoteF64) }
2002-
mutating func visitF64ConvertI32S() throws -> Output { try visitConversion(.i32, .f64, Instruction.f64ConvertI32S) }
2003-
mutating func visitF64ConvertI32U() throws -> Output { try visitConversion(.i32, .f64, Instruction.f64ConvertI32U) }
2004-
mutating func visitF64ConvertI64S() throws -> Output { try visitConversion(.i64, .f64, Instruction.f64ConvertI64S) }
2005-
mutating func visitF64ConvertI64U() throws -> Output { try visitConversion(.i64, .f64, Instruction.f64ConvertI64U) }
2006-
mutating func visitF64PromoteF32() throws -> Output { try visitConversion(.f32, .f64, Instruction.f64PromoteF32) }
2007-
mutating func visitI32ReinterpretF32() throws -> Output { try visitConversion(.f32, .i32, Instruction.i32ReinterpretF32) }
2008-
mutating func visitI64ReinterpretF64() throws -> Output { try visitConversion(.f64, .i64, Instruction.i64ReinterpretF64) }
2009-
mutating func visitF32ReinterpretI32() throws -> Output { try visitConversion(.i32, .f32, Instruction.f32ReinterpretI32) }
2010-
mutating func visitF64ReinterpretI64() throws -> Output { try visitConversion(.i64, .f64, Instruction.f64ReinterpretI64) }
1986+
mutating func visitConversion(_ conversion: WasmParser.Instruction.Conversion) throws {
1987+
let from: ValueType, to: ValueType, instruction: (Instruction.UnaryOperand) -> Instruction
1988+
switch conversion {
1989+
case .i32WrapI64: (from, to, instruction) = (.i64, .i32, Instruction.i32WrapI64)
1990+
case .i32TruncF32S: (from, to, instruction) = (.f32, .i32, Instruction.i32TruncF32S)
1991+
case .i32TruncF32U: (from, to, instruction) = (.f32, .i32, Instruction.i32TruncF32U)
1992+
case .i32TruncF64S: (from, to, instruction) = (.f64, .i32, Instruction.i32TruncF64S)
1993+
case .i32TruncF64U: (from, to, instruction) = (.f64, .i32, Instruction.i32TruncF64U)
1994+
case .i64ExtendI32S: (from, to, instruction) = (.i32, .i64, Instruction.i64ExtendI32S)
1995+
case .i64ExtendI32U: (from, to, instruction) = (.i32, .i64, Instruction.i64ExtendI32U)
1996+
case .i64TruncF32S: (from, to, instruction) = (.f32, .i64, Instruction.i64TruncF32S)
1997+
case .i64TruncF32U: (from, to, instruction) = (.f32, .i64, Instruction.i64TruncF32U)
1998+
case .i64TruncF64S: (from, to, instruction) = (.f64, .i64, Instruction.i64TruncF64S)
1999+
case .i64TruncF64U: (from, to, instruction) = (.f64, .i64, Instruction.i64TruncF64U)
2000+
case .f32ConvertI32S: (from, to, instruction) = (.i32, .f32, Instruction.f32ConvertI32S)
2001+
case .f32ConvertI32U: (from, to, instruction) = (.i32, .f32, Instruction.f32ConvertI32U)
2002+
case .f32ConvertI64S: (from, to, instruction) = (.i64, .f32, Instruction.f32ConvertI64S)
2003+
case .f32ConvertI64U: (from, to, instruction) = (.i64, .f32, Instruction.f32ConvertI64U)
2004+
case .f32DemoteF64: (from, to, instruction) = (.f64, .f32, Instruction.f32DemoteF64)
2005+
case .f64ConvertI32S: (from, to, instruction) = (.i32, .f64, Instruction.f64ConvertI32S)
2006+
case .f64ConvertI32U: (from, to, instruction) = (.i32, .f64, Instruction.f64ConvertI32U)
2007+
case .f64ConvertI64S: (from, to, instruction) = (.i64, .f64, Instruction.f64ConvertI64S)
2008+
case .f64ConvertI64U: (from, to, instruction) = (.i64, .f64, Instruction.f64ConvertI64U)
2009+
case .f64PromoteF32: (from, to, instruction) = (.f32, .f64, Instruction.f64PromoteF32)
2010+
case .i32ReinterpretF32: (from, to, instruction) = (.f32, .i32, Instruction.i32ReinterpretF32)
2011+
case .i64ReinterpretF64: (from, to, instruction) = (.f64, .i64, Instruction.i64ReinterpretF64)
2012+
case .f32ReinterpretI32: (from, to, instruction) = (.i32, .f32, Instruction.f32ReinterpretI32)
2013+
case .f64ReinterpretI64: (from, to, instruction) = (.i64, .f64, Instruction.f64ReinterpretI64)
2014+
case .i32TruncSatF32S: (from, to, instruction) = (.f32, .i32, Instruction.i32TruncSatF32S)
2015+
case .i32TruncSatF32U: (from, to, instruction) = (.f32, .i32, Instruction.i32TruncSatF32U)
2016+
case .i32TruncSatF64S: (from, to, instruction) = (.f64, .i32, Instruction.i32TruncSatF64S)
2017+
case .i32TruncSatF64U: (from, to, instruction) = (.f64, .i32, Instruction.i32TruncSatF64U)
2018+
case .i64TruncSatF32S: (from, to, instruction) = (.f32, .i64, Instruction.i64TruncSatF32S)
2019+
case .i64TruncSatF32U: (from, to, instruction) = (.f32, .i64, Instruction.i64TruncSatF32U)
2020+
case .i64TruncSatF64S: (from, to, instruction) = (.f64, .i64, Instruction.i64TruncSatF64S)
2021+
case .i64TruncSatF64U: (from, to, instruction) = (.f64, .i64, Instruction.i64TruncSatF64U)
2022+
}
2023+
try visitConversion(from, to, instruction)
2024+
}
20112025

20122026
mutating func visitMemoryInit(dataIndex: UInt32) throws -> Output {
20132027
try self.module.validateDataSegment(dataIndex)
@@ -2173,14 +2187,6 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
21732187
return .tableSize(Instruction.TableSizeOperand(tableIndex: table, result: LVReg(result)))
21742188
}
21752189
}
2176-
mutating func visitI32TruncSatF32S() throws -> Output { try visitConversion(.f32, .i32, Instruction.i32TruncSatF32S) }
2177-
mutating func visitI32TruncSatF32U() throws -> Output { try visitConversion(.f32, .i32, Instruction.i32TruncSatF32U) }
2178-
mutating func visitI32TruncSatF64S() throws -> Output { try visitConversion(.f64, .i32, Instruction.i32TruncSatF64S) }
2179-
mutating func visitI32TruncSatF64U() throws -> Output { try visitConversion(.f64, .i32, Instruction.i32TruncSatF64U) }
2180-
mutating func visitI64TruncSatF32S() throws -> Output { try visitConversion(.f32, .i64, Instruction.i64TruncSatF32S) }
2181-
mutating func visitI64TruncSatF32U() throws -> Output { try visitConversion(.f32, .i64, Instruction.i64TruncSatF32U) }
2182-
mutating func visitI64TruncSatF64S() throws -> Output { try visitConversion(.f64, .i64, Instruction.i64TruncSatF64S) }
2183-
mutating func visitI64TruncSatF64U() throws -> Output { try visitConversion(.f64, .i64, Instruction.i64TruncSatF64U) }
21842190
}
21852191

21862192
struct TranslationError: Error, CustomStringConvertible {

0 commit comments

Comments
 (0)