diff --git a/Makefile b/Makefile index 9d6096f..034df9d 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ BUILD_DIR := build # Project-local nimcache so CI can cache compiles (default is ~/.cache/nim). NIMFLAGS ?= --nimcache:nimcache -EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ownership_checked ownership_release drop_early_return lifetime_elision ctfe ctfe_crc async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp iter_generic generic_infer_hof struct_tuple_pat match_block nested_patterns match_guards pattern_shadow move_field move_field_partial move_field_remaining move_field_nested move_field_ptr move_cross_fn c_precedence macro_twice macro_repeat macro_nested macro_hygiene macro_unhygienic macro_stmt_pat macro_tt macro_tt_raw macro_type collections_extra generic_enum switch +EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ownership_checked ownership_release drop_early_return lifetime_elision ctfe ctfe_crc async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp iter_generic generic_infer_hof struct_tuple_pat match_block nested_patterns match_guards pattern_shadow move_field move_field_partial move_field_remaining move_field_nested move_field_ptr move_cross_fn c_precedence macro_twice macro_repeat macro_nested macro_hygiene macro_unhygienic macro_stmt_pat macro_tt macro_tt_raw macro_type collections_extra generic_enum switch is_operator # Platform smoke (macOS CI): full EXAMPLES still runs on Linux. EXAMPLES_SMOKE := hello ownership ownership_release strings map move_field move_field_partial move_field_remaining move_field_nested move_field_ptr move_cross_fn c_precedence macro_twice macro_repeat macro_nested macro_hygiene macro_unhygienic macro_stmt_pat macro_tt macro_tt_raw ctfe_crc diff --git a/bootstrap/c_backend.nim b/bootstrap/c_backend.nim index cd875fa..10ffcde 100644 --- a/bootstrap/c_backend.nim +++ b/bootstrap/c_backend.nim @@ -346,7 +346,8 @@ proc emitExpr(be: var CBackend, node: HirNode): string = return &"(({typ}){operand})" of hIs: - return "true" # TODO: proper type checking + # Should be desugared in hir_lower; keep false if any residual hIs remains + return "false" of hSizeOf: let typ = typeToC(be, node.sizeOfType) diff --git a/bootstrap/hir_lower.nim b/bootstrap/hir_lower.nim index d684262..81995b4 100644 --- a/bootstrap/hir_lower.nim +++ b/bootstrap/hir_lower.nim @@ -2053,37 +2053,110 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode = typ: typ, loc: loc) of ekIs: + # Desugar `expr is Variant` to a tag comparison so LIR/C backends need no hIs. + # Simple enums (no data): subject == Enum_Variant + # Algebraic enums: subject.tag == Enum_Variant let operand = ctx.lowerExpr(expr.exprIsOperand) - var isType = makeUnknown() + var variantName = "" if expr.exprIsType != nil and expr.exprIsType.kind == tekNamed: - isType = makeNamed(expr.exprIsType.typeName) - return HirNode(kind: hIs, isOperand: operand, isType: isType, + variantName = expr.exprIsType.typeName + var enumName = "" + var opType = ctx.resolveExprType(expr.exprIsOperand) + # Prefer TypeExpr with type args so generic enums monomorphize (Result_int_String) + if expr.exprIsOperand != nil and expr.exprIsOperand.kind == ekIdent: + if ctx.varTypeExprs.hasKey(expr.exprIsOperand.exprIdent): + let te = ctx.varTypeExprs[expr.exprIsOperand.exprIdent] + if te != nil: + let resolved = ctx.resolveTypeExpr(te) + if resolved != nil and resolved.kind == tkNamed and resolved.name.len > 0: + opType = resolved + if opType != nil and opType.kind == tkNamed: + enumName = opType.name + if enumName.len > 0 and variantName.len > 0: + var baseName = enumName + if ctx.structInstMap.hasKey(enumName): + baseName = ctx.structInstMap[enumName].baseName + var hasData = ctx.enumHasDataVariants(baseName) + if not hasData: + hasData = ctx.enumHasDataVariants(enumName) + # Monomorphized data enums always have tag+data layout + if not hasData and ctx.structInstMap.hasKey(enumName): + hasData = true + let tagName = enumName & "_" & variantName + if hasData: + let tagField = HirNode(kind: hFieldPtr, fieldPtrBase: operand, fieldName: "tag", + typ: makePointer(makeNamed(enumName & "_Tag")), loc: loc) + let tagLoad = HirNode(kind: hLoad, loadPtr: tagField, + typ: makeNamed(enumName & "_Tag"), loc: loc) + let tagConst = hirVar(tagName, makeNamed(enumName & "_Tag"), loc) + return hirBinary(tkEq, tagLoad, tagConst, makeBool(), loc) + else: + let tagConst = hirVar(tagName, makeNamed(enumName), loc) + return hirBinary(tkEq, operand, tagConst, makeBool(), loc) + # Non-enum / unresolved: false + return HirNode(kind: hLit, + litToken: Token(kind: tkBoolLiteral, text: "false", loc: loc), typ: makeBool(), loc: loc) of ekTry: let operand = ctx.lowerExpr(expr.exprTryOperand) - let operandType = ctx.resolveExprType(expr.exprTryOperand) + var operandType = ctx.resolveExprType(expr.exprTryOperand) var typeName = "" var errTag = "" var okField = "" - if operandType.kind == tkNamed: + if operandType != nil and operandType.kind == tkNamed: typeName = operandType.name - case typeName - of "Result": - errTag = "Result_Err" - okField = "Ok_0" - of "Option": - errTag = "Option_None" - okField = "Some_0" - else: - errTag = typeName & "_Err" - okField = "Ok_0" else: - errTag = "Result_Err" - okField = "Ok_0" typeName = "Result" + # Upgrade bare generic enum name to concrete monomorphization. + # Sema stores Result/Option without mangled type-args; try needs Result_int_String_Tag. + if ctx.genericEnums.hasKey(typeName): + # Prefer resolving call/ident TypeExpr with type args + if expr.exprTryOperand != nil: + if expr.exprTryOperand.kind == ekIdent and ctx.varTypeExprs.hasKey(expr.exprTryOperand.exprIdent): + let te = ctx.varTypeExprs[expr.exprTryOperand.exprIdent] + if te != nil: + let resolved = ctx.resolveTypeExpr(te) + if resolved != nil and resolved.kind == tkNamed and resolved.name.startsWith(typeName & "_"): + typeName = resolved.name + elif expr.exprTryOperand.kind == ekCall and expr.exprTryOperand.exprCallCallee != nil and + expr.exprTryOperand.exprCallCallee.kind == ekIdent: + let calSym = ctx.globalScope.lookup(expr.exprTryOperand.exprCallCallee.exprIdent) + if calSym != nil and calSym.decl != nil and calSym.decl.kind == dkFunc and + calSym.decl.declFuncReturnType != nil: + let resolved = ctx.resolveTypeExpr(calSym.decl.declFuncReturnType) + if resolved != nil and resolved.kind == tkNamed and + (resolved.name == typeName or resolved.name.startsWith(typeName & "_")): + typeName = resolved.name + # Enclosing function return type (must match for `?` propagation) + let stillBare = operandType == nil or operandType.kind != tkNamed or + typeName == operandType.name + if stillBare and ctx.currentFuncRetType != nil and + ctx.currentFuncRetType.kind == tkNamed: + let rn = ctx.currentFuncRetType.name + if rn.startsWith(typeName & "_"): + typeName = rn + operandType = makeNamed(typeName) + + # Err tag / Ok field from base or concrete name + let baseForTags = + if ctx.structInstMap.hasKey(typeName): ctx.structInstMap[typeName].baseName + elif ctx.genericEnums.hasKey(typeName): typeName + else: typeName + if baseForTags == "Option" or typeName.startsWith("Option_"): + errTag = typeName & "_None" + if typeName == "Option": errTag = "Option_None" + okField = "Some_0" + elif baseForTags == "Result" or typeName.startsWith("Result_"): + errTag = typeName & "_Err" + if typeName == "Result": errTag = "Result_Err" + okField = "Ok_0" + else: + errTag = typeName & "_Err" + okField = "Ok_0" + let tmpName = ctx.freshTryVar() let tmpAlloca = hirAlloca(tmpName, operandType, loc) let tmpVar = hirVar(tmpName, operandType, loc) @@ -3084,8 +3157,10 @@ proc lowerModule*(module: Module, sema: Sema): HirModule = if en.name.startsWith(enumName & "_"): return en.name & "_" & rest - # Also substitute type names in hAlloca and hStructInit from extraEnums + # Substitute type names in Type fields (Result → Result_int_String, + # Result_Tag → Result_int_String_Tag, Result_Data → Result_int_String_Data). proc substEnumType(typ: var Type, ctx: LowerCtx) = + if typ == nil: return if typ.kind == tkNamed: for enumName, _ in ctx.genericEnums: if typ.name == enumName: @@ -3093,15 +3168,32 @@ proc lowerModule*(module: Module, sema: Sema): HirModule = if en.name.startsWith(enumName & "_"): typ = makeNamed(en.name) return + # Suffix forms used by try/field lowering + let tagSuffix = enumName & "_Tag" + let dataSuffix = enumName & "_Data" + if typ.name == tagSuffix or typ.name == dataSuffix: + let rest = typ.name[enumName.len + 1 .. ^1] # "Tag" or "Data" + for en in ctx.extraEnums: + if en.name.startsWith(enumName & "_"): + typ = makeNamed(en.name & "_" & rest) + return + elif typ.kind in {tkPointer, tkRef, tkMutRef, tkSlice} and typ.inner.len > 0: + var inner = typ.inner[0] + substEnumType(inner, ctx) + typ.inner[0] = inner proc mangleHirNode(n: HirNode, ctx: LowerCtx) = if n == nil: return + # Mangle type annotation on every node (temps for .tag/.data loads) + if n.typ != nil: + substEnumType(n.typ, ctx) case n.kind of hVar: n.varName = substEnumName(n.varName, ctx) of hStructInit: n.structInitName = substEnumName(n.structInitName, ctx) of hFieldAccess: n.fieldAccessName = substEnumName(n.fieldAccessName, ctx) of hArrowField: n.arrowFieldName = substEnumName(n.arrowFieldName, ctx) of hAlloca: substEnumType(n.allocaType, ctx) + of hCast: substEnumType(n.castType, ctx) else: discard # Walk children by variant case n.kind diff --git a/docs/IMPROVEMENTS.md b/docs/IMPROVEMENTS.md index 87c8120..4fd7dee 100644 --- a/docs/IMPROVEMENTS.md +++ b/docs/IMPROVEMENTS.md @@ -34,6 +34,15 @@ --- +## Follow-up fixes (post DeepSeek session) + +| # | Бъг | Фикс | +|---|-----|------| +| F.1 | `is` → LIR `unhandled hIs` / always false | Desugar to `==` / `.tag ==` in bootstrap + selfhost | +| F.2 | `?` + `Result` → `Result_Tag` C error | Concrete monomorphized typeName + `_Tag`/`_Data` mangling | +| F.3 | `Unwrap` panic continues with garbage | `bux_exit(1)` after panic in Result/Option | +| F.4 | Regression example | `examples/is_operator.bux` | + ## Резултат - **Всички тестове: 0 FAIL, 0 error** @@ -44,6 +53,8 @@ - Data field достъп (`p.data.First_0` като l-value и r-value) - Множество конкретни инстанции в един файл - `Result` и `Option` в stdlib + - `is` operator (simple + algebraic enums) + - `?` try operator with monomorphized `Result` ## Пример който работи diff --git a/examples/is_operator.bux b/examples/is_operator.bux new file mode 100644 index 0000000..0fa406e --- /dev/null +++ b/examples/is_operator.bux @@ -0,0 +1,40 @@ +// is_operator.bux — `expr is Variant` for simple and algebraic enums +import Std::Io::{PrintLine, PrintInt}; + +enum Color { + Red, + Green, + Blue, +} + +enum Box { + Val(int), + Empty, +} + +func Main() -> int { + let c: Color = Color { tag: Color_Red }; + if c is Red { + PrintLine("color-red"); + } + if c is Blue { + PrintLine("color-blue-unexpected"); + } else { + PrintLine("color-not-blue"); + } + + let b: Box = Box { tag: Box_Val }; + b.data.Val_0 = 42; + if b is Val { + Print("box-val="); + PrintInt(b.data.Val_0 as int64); + PrintLine(""); + } + if b is Empty { + PrintLine("box-empty-unexpected"); + } else { + PrintLine("box-not-empty"); + } + + return 0; +} diff --git a/lib/Option.bux b/lib/Option.bux index 0f532fb..1ce679a 100644 --- a/lib/Option.bux +++ b/lib/Option.bux @@ -29,6 +29,7 @@ module Std::Option { func Option_Unwrap(o: Option) -> T { if o.tag != Option_Some { PrintLine("panic: unwrap on None"); + bux_exit(1); } return o.data.Some_0; } diff --git a/lib/Result.bux b/lib/Result.bux index 5ae3866..c278172 100644 --- a/lib/Result.bux +++ b/lib/Result.bux @@ -31,6 +31,7 @@ module Std::Result { func Result_Unwrap(r: Result) -> T { if r.tag != Result_Ok { PrintLine("panic: unwrap on Err"); + bux_exit(1); } return r.data.Ok_0; } @@ -53,6 +54,7 @@ module Std::Result { func Result_UnwrapErr(r: Result) -> E { if r.tag != Result_Err { PrintLine("panic: unwrap_err on Ok"); + bux_exit(1); } return r.data.Err_0; } diff --git a/src/c_backend.bux b/src/c_backend.bux index fdd027a..470e9c9 100644 --- a/src/c_backend.bux +++ b/src/c_backend.bux @@ -1498,10 +1498,8 @@ module CBackend { return; } - // Is (type test): check if the tag of an enum matches a variant + // Is (type test): should be desugared to hBinary in HIR lowering if kind == hIs { - // Should have been lowered to hBinary in HIR lowering - // Fallback: always emit false StringBuilder_Append(&cbe.sb, "0"); return; } diff --git a/src/hir_lower.bux b/src/hir_lower.bux index 45cd180..380e3fb 100644 --- a/src/hir_lower.bux +++ b/src/hir_lower.bux @@ -803,16 +803,50 @@ module HirLower { func Lcx_EnumHasData(ctx: *LowerCtx, enumName: String) -> bool { if String_Eq(enumName, "") { return false; } let sym: Symbol = Scope_Lookup(ctx.scope, enumName); - if sym.decl == null as *Decl || sym.decl.kind != dkEnum { return false; } - if sym.decl.variantCount > 0 && sym.decl.variant0.fieldCount > 0 { return true; } - if sym.decl.variantCount > 1 && sym.decl.variant1.fieldCount > 0 { return true; } - if sym.decl.variantCount > 2 && sym.decl.variant2.fieldCount > 0 { return true; } - if sym.decl.variantCount > 3 && sym.decl.variant3.fieldCount > 0 { return true; } - if sym.decl.variantCount > 4 && sym.decl.variant4.fieldCount > 0 { return true; } - if sym.decl.variantCount > 5 && sym.decl.variant5.fieldCount > 0 { return true; } - if sym.decl.variantCount > 6 && sym.decl.variant6.fieldCount > 0 { return true; } - if sym.decl.variantCount > 7 && sym.decl.variant7.fieldCount > 0 { return true; } - if sym.decl.variantCount > 8 && sym.decl.variant8.fieldCount > 0 { return true; } + if sym.decl != null as *Decl && sym.decl.kind == dkEnum { + if sym.decl.variantCount > 0 && sym.decl.variant0.fieldCount > 0 { return true; } + if sym.decl.variantCount > 1 && sym.decl.variant1.fieldCount > 0 { return true; } + if sym.decl.variantCount > 2 && sym.decl.variant2.fieldCount > 0 { return true; } + if sym.decl.variantCount > 3 && sym.decl.variant3.fieldCount > 0 { return true; } + if sym.decl.variantCount > 4 && sym.decl.variant4.fieldCount > 0 { return true; } + if sym.decl.variantCount > 5 && sym.decl.variant5.fieldCount > 0 { return true; } + if sym.decl.variantCount > 6 && sym.decl.variant6.fieldCount > 0 { return true; } + if sym.decl.variantCount > 7 && sym.decl.variant7.fieldCount > 0 { return true; } + if sym.decl.variantCount > 8 && sym.decl.variant8.fieldCount > 0 { return true; } + return false; + } + // Monomorphized instance (Result_int_String) — check HIR enums + if ctx.hm != null as *HirModule { + var i: int = 0; + while i < ctx.hm.enumCount { + if String_Eq(ctx.hm.enums[i].name, enumName) { + var vi: int = 0; + while vi < ctx.hm.enums[i].variantCount { + if ctx.hm.enums[i].variants[vi].fieldCount > 0 { return true; } + vi = vi + 1; + } + return false; + } + i = i + 1; + } + } + // Bare prefix of monomorphized generic enum: Result_int_String → Result + var gi: int = 0; + while gi < ctx.genStructCount { + if ctx.genStructs[gi].kind == dkEnum { + let base: String = ctx.genStructs[gi].strValue; + let prefix: String = String_Concat(base, "_"); + let prefLen: int = String_Len(prefix) as int; + let nameLen: int = String_Len(enumName) as int; + if nameLen > prefLen { + let p: String = bux_str_slice(enumName, 0, prefLen as uint); + if String_Eq(p, prefix) { + return Lcx_EnumHasData(ctx, base); + } + } + } + gi = gi + 1; + } return false; } @@ -2509,52 +2543,73 @@ module HirLower { return n; } - // Is (type test): expr is Type — lowered to tag check for enums + // Is (type test): expr is Variant — desugar to tag / value equality + // Simple enums: subject == Enum_Variant + // Algebraic enums: subject.tag == Enum_Variant if kind == ekIs { let operand: *HirNode = Lcx_LowerExpr(ctx, expr.child1); - if expr.refType != null as *TypeExpr { - let isType: String = ""; - if !String_Eq(expr.refType.typeName, "") { - isType = expr.refType.typeName; - } - if !String_Eq(isType, "") { - // Check if operand is an enum type — resolve from HIR type info - let enumName: String = ""; - if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { - let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue); - if sym.decl != null as *Decl && sym.decl.kind == dkEnum { - enumName = expr.child1.strValue; - } - } - if !String_Eq(enumName, "") { - let tagName: String = String_Concat(String_Concat(enumName, "_"), isType); - // tagPtr = operand.tag - let tagPtr: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; - tagPtr.kind = hFieldPtr; - tagPtr.line = expr.line; - tagPtr.column = expr.column; - tagPtr.strValue = "tag"; - tagPtr.child1 = operand; - // tagLoad = *tagPtr - let tagLoad: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; - tagLoad.kind = hLoad; - tagLoad.line = expr.line; - tagLoad.column = expr.column; - tagLoad.child1 = tagPtr; - // tagConst = Enum_Target - let tagConst: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; - tagConst.kind = hVar; - tagConst.line = expr.line; - tagConst.column = expr.column; - tagConst.strValue = tagName; - let result: *HirNode = Lcx_MakeBinHir(tkEq, tagLoad, tagConst, expr.line, expr.column); - result.sourceFile = ctx.currentSourceFile; - return result; + var variantName: String = ""; + if expr.refType != null as *TypeExpr && !String_Eq(expr.refType.typeName, "") { + variantName = expr.refType.typeName; + } + var enumName: String = ""; + // Resolve operand type from scope (variable / param), not enum decl name + if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { + let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue); + if !String_Eq(sym.typeName, "") { + enumName = sym.typeName; + } else if sym.refType != null as *TypeExpr { + let te: *TypeExpr = Lcx_SubstituteType(ctx, sym.refType); + if te != null as *TypeExpr && !String_Eq(te.typeName, "") { + enumName = te.typeName; } } } - // Fallback: emit a compile-time error diagnostic via HIR comment - // For non-enum types, is always returns false at runtime + // Fallback: type annotation on the is-expression operand + if String_Eq(enumName, "") && expr.child1 != null as *Expr && + expr.child1.refType != null as *TypeExpr { + let te: *TypeExpr = Lcx_SubstituteType(ctx, expr.child1.refType); + if te != null as *TypeExpr && !String_Eq(te.typeName, "") { + enumName = te.typeName; + } + } + if !String_Eq(enumName, "") && !String_Eq(variantName, "") { + let hasData: bool = Lcx_EnumHasData(ctx, enumName); + let tagName: String = String_Concat(String_Concat(enumName, "_"), variantName); + if hasData { + // subject.tag == Enum_Variant + let tagPtr: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; + tagPtr.kind = hFieldPtr; + tagPtr.line = expr.line; + tagPtr.column = expr.column; + tagPtr.strValue = "tag"; + tagPtr.child1 = operand; + let tagLoad: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; + tagLoad.kind = hLoad; + tagLoad.line = expr.line; + tagLoad.column = expr.column; + tagLoad.child1 = tagPtr; + let tagConst: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; + tagConst.kind = hVar; + tagConst.line = expr.line; + tagConst.column = expr.column; + tagConst.strValue = tagName; + let result: *HirNode = Lcx_MakeBinHir(tkEq, tagLoad, tagConst, expr.line, expr.column); + result.sourceFile = ctx.currentSourceFile; + return result; + } else { + // Simple enum: subject == Enum_Variant + let tagConst: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; + tagConst.kind = hVar; + tagConst.line = expr.line; + tagConst.column = expr.column; + tagConst.strValue = tagName; + let result: *HirNode = Lcx_MakeBinHir(tkEq, operand, tagConst, expr.line, expr.column); + result.sourceFile = ctx.currentSourceFile; + return result; + } + } + // Non-enum / unresolved → false let result: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; result.kind = hLit; result.line = expr.line; @@ -2668,19 +2723,43 @@ module HirLower { if stmt.child1 != null as *Expr && stmt.child1.kind == ekTry { let tryExpr: *Expr = stmt.child1; let operandExpr: *Expr = tryExpr.child1; - let operandTypeExpr: *TypeExpr = operandExpr.refType; + let operandTypeExpr: *TypeExpr = if operandExpr != null as *Expr { operandExpr.refType } else { null as *TypeExpr }; var typeName: String = "Result"; var errTag: String = "Result_Err"; var okField: String = "Ok_0"; if operandTypeExpr != null as *TypeExpr && operandTypeExpr.kind == tekNamed { - typeName = operandTypeExpr.typeName; + // Substitute/mangle generic enum type args (Result → Result_int_String) + let subTe: *TypeExpr = Lcx_SubstituteType(ctx, operandTypeExpr); + if subTe != null as *TypeExpr && !String_Eq(subTe.typeName, "") { + typeName = subTe.typeName; + } else { + typeName = operandTypeExpr.typeName; + } + } + // Detect Option vs Result (bare or monomorphized) + var isOption: bool = String_Eq(typeName, "Option"); + if !isOption { + let optPrefix: String = "Option_"; + let tnLen: int = String_Len(typeName) as int; + let prefLen: int = String_Len(optPrefix) as int; + if tnLen > prefLen { + let p: String = bux_str_slice(typeName, 0, prefLen as uint); + if String_Eq(p, optPrefix) { isOption = true; } + } + } + if isOption { if String_Eq(typeName, "Option") { errTag = "Option_None"; - okField = "Some_0"; - } else if !String_Eq(typeName, "Result") { - errTag = String_Concat(String_Concat(typeName, "_"), "Err"); - okField = "Ok_0"; + } else { + errTag = String_Concat(typeName, "_None"); } + okField = "Some_0"; + } else if String_Eq(typeName, "Result") { + errTag = "Result_Err"; + okField = "Ok_0"; + } else { + errTag = String_Concat(typeName, "_Err"); + okField = "Ok_0"; } let tmpName: String = String_Concat("__try_tmp_", String_FromInt(ctx.tryCounter as int64)); ctx.tryCounter = ctx.tryCounter + 1;