fix: is-operator, try with generic Result, Unwrap exits on panic
ci / build (ubuntu) (push) Has been cancelled
ci / unit + fmt (push) Has been cancelled
ci / examples (push) Has been cancelled
ci / goldens + tools (push) Has been cancelled
ci / apps (push) Has been cancelled
ci / selfhost smoke (push) Has been cancelled
ci / macos smoke (push) Has been cancelled
ci / windows smoke (push) Has been cancelled
ci / CI gate (push) Has been cancelled
selfhost-loop / bootstrap determinism (push) Has been cancelled
ci / build (ubuntu) (push) Has been cancelled
ci / unit + fmt (push) Has been cancelled
ci / examples (push) Has been cancelled
ci / goldens + tools (push) Has been cancelled
ci / apps (push) Has been cancelled
ci / selfhost smoke (push) Has been cancelled
ci / macos smoke (push) Has been cancelled
ci / windows smoke (push) Has been cancelled
ci / CI gate (push) Has been cancelled
selfhost-loop / bootstrap determinism (push) Has been cancelled
Desugar `is` to tag/value equality in bootstrap and selfhost so LIR no longer drops hIs as false. Resolve monomorphized Result/Option type names for `?` (_Tag/_Data). Call bux_exit(1) after Unwrap panic messages. Add is_operator example and document follow-up fixes.
This commit is contained in:
@@ -5,7 +5,7 @@ BUILD_DIR := build
|
|||||||
# Project-local nimcache so CI can cache compiles (default is ~/.cache/nim).
|
# Project-local nimcache so CI can cache compiles (default is ~/.cache/nim).
|
||||||
NIMFLAGS ?= --nimcache:nimcache
|
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.
|
# 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
|
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
|
||||||
|
|||||||
@@ -346,7 +346,8 @@ proc emitExpr(be: var CBackend, node: HirNode): string =
|
|||||||
return &"(({typ}){operand})"
|
return &"(({typ}){operand})"
|
||||||
|
|
||||||
of hIs:
|
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:
|
of hSizeOf:
|
||||||
let typ = typeToC(be, node.sizeOfType)
|
let typ = typeToC(be, node.sizeOfType)
|
||||||
|
|||||||
+108
-16
@@ -2053,36 +2053,109 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
typ: typ, loc: loc)
|
typ: typ, loc: loc)
|
||||||
|
|
||||||
of ekIs:
|
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)
|
let operand = ctx.lowerExpr(expr.exprIsOperand)
|
||||||
var isType = makeUnknown()
|
var variantName = ""
|
||||||
if expr.exprIsType != nil and expr.exprIsType.kind == tekNamed:
|
if expr.exprIsType != nil and expr.exprIsType.kind == tekNamed:
|
||||||
isType = makeNamed(expr.exprIsType.typeName)
|
variantName = expr.exprIsType.typeName
|
||||||
return HirNode(kind: hIs, isOperand: operand, isType: isType,
|
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)
|
typ: makeBool(), loc: loc)
|
||||||
|
|
||||||
of ekTry:
|
of ekTry:
|
||||||
let operand = ctx.lowerExpr(expr.exprTryOperand)
|
let operand = ctx.lowerExpr(expr.exprTryOperand)
|
||||||
let operandType = ctx.resolveExprType(expr.exprTryOperand)
|
var operandType = ctx.resolveExprType(expr.exprTryOperand)
|
||||||
|
|
||||||
var typeName = ""
|
var typeName = ""
|
||||||
var errTag = ""
|
var errTag = ""
|
||||||
var okField = ""
|
var okField = ""
|
||||||
if operandType.kind == tkNamed:
|
if operandType != nil and operandType.kind == tkNamed:
|
||||||
typeName = operandType.name
|
typeName = operandType.name
|
||||||
case typeName
|
else:
|
||||||
of "Result":
|
typeName = "Result"
|
||||||
errTag = "Result_Err"
|
|
||||||
okField = "Ok_0"
|
# Upgrade bare generic enum name to concrete monomorphization.
|
||||||
of "Option":
|
# Sema stores Result/Option without mangled type-args; try needs Result_int_String_Tag.
|
||||||
errTag = "Option_None"
|
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"
|
okField = "Some_0"
|
||||||
|
elif baseForTags == "Result" or typeName.startsWith("Result_"):
|
||||||
|
errTag = typeName & "_Err"
|
||||||
|
if typeName == "Result": errTag = "Result_Err"
|
||||||
|
okField = "Ok_0"
|
||||||
else:
|
else:
|
||||||
errTag = typeName & "_Err"
|
errTag = typeName & "_Err"
|
||||||
okField = "Ok_0"
|
okField = "Ok_0"
|
||||||
else:
|
|
||||||
errTag = "Result_Err"
|
|
||||||
okField = "Ok_0"
|
|
||||||
typeName = "Result"
|
|
||||||
|
|
||||||
let tmpName = ctx.freshTryVar()
|
let tmpName = ctx.freshTryVar()
|
||||||
let tmpAlloca = hirAlloca(tmpName, operandType, loc)
|
let tmpAlloca = hirAlloca(tmpName, operandType, loc)
|
||||||
@@ -3084,8 +3157,10 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
if en.name.startsWith(enumName & "_"):
|
if en.name.startsWith(enumName & "_"):
|
||||||
return en.name & "_" & rest
|
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) =
|
proc substEnumType(typ: var Type, ctx: LowerCtx) =
|
||||||
|
if typ == nil: return
|
||||||
if typ.kind == tkNamed:
|
if typ.kind == tkNamed:
|
||||||
for enumName, _ in ctx.genericEnums:
|
for enumName, _ in ctx.genericEnums:
|
||||||
if typ.name == enumName:
|
if typ.name == enumName:
|
||||||
@@ -3093,15 +3168,32 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
if en.name.startsWith(enumName & "_"):
|
if en.name.startsWith(enumName & "_"):
|
||||||
typ = makeNamed(en.name)
|
typ = makeNamed(en.name)
|
||||||
return
|
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) =
|
proc mangleHirNode(n: HirNode, ctx: LowerCtx) =
|
||||||
if n == nil: return
|
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
|
case n.kind
|
||||||
of hVar: n.varName = substEnumName(n.varName, ctx)
|
of hVar: n.varName = substEnumName(n.varName, ctx)
|
||||||
of hStructInit: n.structInitName = substEnumName(n.structInitName, ctx)
|
of hStructInit: n.structInitName = substEnumName(n.structInitName, ctx)
|
||||||
of hFieldAccess: n.fieldAccessName = substEnumName(n.fieldAccessName, ctx)
|
of hFieldAccess: n.fieldAccessName = substEnumName(n.fieldAccessName, ctx)
|
||||||
of hArrowField: n.arrowFieldName = substEnumName(n.arrowFieldName, ctx)
|
of hArrowField: n.arrowFieldName = substEnumName(n.arrowFieldName, ctx)
|
||||||
of hAlloca: substEnumType(n.allocaType, ctx)
|
of hAlloca: substEnumType(n.allocaType, ctx)
|
||||||
|
of hCast: substEnumType(n.castType, ctx)
|
||||||
else: discard
|
else: discard
|
||||||
# Walk children by variant
|
# Walk children by variant
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|||||||
@@ -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<T,E>` → `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**
|
- **Всички тестове: 0 FAIL, 0 error**
|
||||||
@@ -44,6 +53,8 @@
|
|||||||
- Data field достъп (`p.data.First_0` като l-value и r-value)
|
- Data field достъп (`p.data.First_0` като l-value и r-value)
|
||||||
- Множество конкретни инстанции в един файл
|
- Множество конкретни инстанции в един файл
|
||||||
- `Result<T,E>` и `Option<T>` в stdlib
|
- `Result<T,E>` и `Option<T>` в stdlib
|
||||||
|
- `is` operator (simple + algebraic enums)
|
||||||
|
- `?` try operator with monomorphized `Result<T,E>`
|
||||||
|
|
||||||
## Пример който работи
|
## Пример който работи
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ module Std::Option {
|
|||||||
func Option_Unwrap<T>(o: Option<T>) -> T {
|
func Option_Unwrap<T>(o: Option<T>) -> T {
|
||||||
if o.tag != Option_Some {
|
if o.tag != Option_Some {
|
||||||
PrintLine("panic: unwrap on None");
|
PrintLine("panic: unwrap on None");
|
||||||
|
bux_exit(1);
|
||||||
}
|
}
|
||||||
return o.data.Some_0;
|
return o.data.Some_0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ module Std::Result {
|
|||||||
func Result_Unwrap<T, E>(r: Result<T, E>) -> T {
|
func Result_Unwrap<T, E>(r: Result<T, E>) -> T {
|
||||||
if r.tag != Result_Ok {
|
if r.tag != Result_Ok {
|
||||||
PrintLine("panic: unwrap on Err");
|
PrintLine("panic: unwrap on Err");
|
||||||
|
bux_exit(1);
|
||||||
}
|
}
|
||||||
return r.data.Ok_0;
|
return r.data.Ok_0;
|
||||||
}
|
}
|
||||||
@@ -53,6 +54,7 @@ module Std::Result {
|
|||||||
func Result_UnwrapErr<T, E>(r: Result<T, E>) -> E {
|
func Result_UnwrapErr<T, E>(r: Result<T, E>) -> E {
|
||||||
if r.tag != Result_Err {
|
if r.tag != Result_Err {
|
||||||
PrintLine("panic: unwrap_err on Ok");
|
PrintLine("panic: unwrap_err on Ok");
|
||||||
|
bux_exit(1);
|
||||||
}
|
}
|
||||||
return r.data.Err_0;
|
return r.data.Err_0;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -1498,10 +1498,8 @@ module CBackend {
|
|||||||
return;
|
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 {
|
if kind == hIs {
|
||||||
// Should have been lowered to hBinary in HIR lowering
|
|
||||||
// Fallback: always emit false
|
|
||||||
StringBuilder_Append(&cbe.sb, "0");
|
StringBuilder_Append(&cbe.sb, "0");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+103
-24
@@ -803,7 +803,7 @@ module HirLower {
|
|||||||
func Lcx_EnumHasData(ctx: *LowerCtx, enumName: String) -> bool {
|
func Lcx_EnumHasData(ctx: *LowerCtx, enumName: String) -> bool {
|
||||||
if String_Eq(enumName, "") { return false; }
|
if String_Eq(enumName, "") { return false; }
|
||||||
let sym: Symbol = Scope_Lookup(ctx.scope, enumName);
|
let sym: Symbol = Scope_Lookup(ctx.scope, enumName);
|
||||||
if sym.decl == null as *Decl || sym.decl.kind != dkEnum { return false; }
|
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 > 0 && sym.decl.variant0.fieldCount > 0 { return true; }
|
||||||
if sym.decl.variantCount > 1 && sym.decl.variant1.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 > 2 && sym.decl.variant2.fieldCount > 0 { return true; }
|
||||||
@@ -815,6 +815,40 @@ module HirLower {
|
|||||||
if sym.decl.variantCount > 8 && sym.decl.variant8.fieldCount > 0 { return true; }
|
if sym.decl.variantCount > 8 && sym.decl.variant8.fieldCount > 0 { return true; }
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
func Lcx_MakeLitHir(litKind: int, litText: String, line: uint32, col: uint32) -> *HirNode {
|
func Lcx_MakeLitHir(litKind: int, litText: String, line: uint32, col: uint32) -> *HirNode {
|
||||||
let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
@@ -2509,39 +2543,52 @@ module HirLower {
|
|||||||
return n;
|
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 {
|
if kind == ekIs {
|
||||||
let operand: *HirNode = Lcx_LowerExpr(ctx, expr.child1);
|
let operand: *HirNode = Lcx_LowerExpr(ctx, expr.child1);
|
||||||
if expr.refType != null as *TypeExpr {
|
var variantName: String = "";
|
||||||
let isType: String = "";
|
if expr.refType != null as *TypeExpr && !String_Eq(expr.refType.typeName, "") {
|
||||||
if !String_Eq(expr.refType.typeName, "") {
|
variantName = expr.refType.typeName;
|
||||||
isType = expr.refType.typeName;
|
|
||||||
}
|
}
|
||||||
if !String_Eq(isType, "") {
|
var enumName: String = "";
|
||||||
// Check if operand is an enum type — resolve from HIR type info
|
// Resolve operand type from scope (variable / param), not enum decl name
|
||||||
let enumName: String = "";
|
|
||||||
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
||||||
let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue);
|
let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue);
|
||||||
if sym.decl != null as *Decl && sym.decl.kind == dkEnum {
|
if !String_Eq(sym.typeName, "") {
|
||||||
enumName = expr.child1.strValue;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !String_Eq(enumName, "") {
|
}
|
||||||
let tagName: String = String_Concat(String_Concat(enumName, "_"), isType);
|
// Fallback: type annotation on the is-expression operand
|
||||||
// tagPtr = operand.tag
|
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;
|
let tagPtr: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
tagPtr.kind = hFieldPtr;
|
tagPtr.kind = hFieldPtr;
|
||||||
tagPtr.line = expr.line;
|
tagPtr.line = expr.line;
|
||||||
tagPtr.column = expr.column;
|
tagPtr.column = expr.column;
|
||||||
tagPtr.strValue = "tag";
|
tagPtr.strValue = "tag";
|
||||||
tagPtr.child1 = operand;
|
tagPtr.child1 = operand;
|
||||||
// tagLoad = *tagPtr
|
|
||||||
let tagLoad: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
let tagLoad: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
tagLoad.kind = hLoad;
|
tagLoad.kind = hLoad;
|
||||||
tagLoad.line = expr.line;
|
tagLoad.line = expr.line;
|
||||||
tagLoad.column = expr.column;
|
tagLoad.column = expr.column;
|
||||||
tagLoad.child1 = tagPtr;
|
tagLoad.child1 = tagPtr;
|
||||||
// tagConst = Enum_Target
|
|
||||||
let tagConst: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
let tagConst: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
tagConst.kind = hVar;
|
tagConst.kind = hVar;
|
||||||
tagConst.line = expr.line;
|
tagConst.line = expr.line;
|
||||||
@@ -2550,11 +2597,19 @@ module HirLower {
|
|||||||
let result: *HirNode = Lcx_MakeBinHir(tkEq, tagLoad, tagConst, expr.line, expr.column);
|
let result: *HirNode = Lcx_MakeBinHir(tkEq, tagLoad, tagConst, expr.line, expr.column);
|
||||||
result.sourceFile = ctx.currentSourceFile;
|
result.sourceFile = ctx.currentSourceFile;
|
||||||
return result;
|
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
|
||||||
// Fallback: emit a compile-time error diagnostic via HIR comment
|
|
||||||
// For non-enum types, is always returns false at runtime
|
|
||||||
let result: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
let result: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
result.kind = hLit;
|
result.kind = hLit;
|
||||||
result.line = expr.line;
|
result.line = expr.line;
|
||||||
@@ -2668,19 +2723,43 @@ module HirLower {
|
|||||||
if stmt.child1 != null as *Expr && stmt.child1.kind == ekTry {
|
if stmt.child1 != null as *Expr && stmt.child1.kind == ekTry {
|
||||||
let tryExpr: *Expr = stmt.child1;
|
let tryExpr: *Expr = stmt.child1;
|
||||||
let operandExpr: *Expr = tryExpr.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 typeName: String = "Result";
|
||||||
var errTag: String = "Result_Err";
|
var errTag: String = "Result_Err";
|
||||||
var okField: String = "Ok_0";
|
var okField: String = "Ok_0";
|
||||||
if operandTypeExpr != null as *TypeExpr && operandTypeExpr.kind == tekNamed {
|
if operandTypeExpr != null as *TypeExpr && operandTypeExpr.kind == tekNamed {
|
||||||
|
// Substitute/mangle generic enum type args (Result<int,String> → 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;
|
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") {
|
if String_Eq(typeName, "Option") {
|
||||||
errTag = "Option_None";
|
errTag = "Option_None";
|
||||||
okField = "Some_0";
|
} else {
|
||||||
} else if !String_Eq(typeName, "Result") {
|
errTag = String_Concat(typeName, "_None");
|
||||||
errTag = String_Concat(String_Concat(typeName, "_"), "Err");
|
|
||||||
okField = "Ok_0";
|
|
||||||
}
|
}
|
||||||
|
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));
|
let tmpName: String = String_Concat("__try_tmp_", String_FromInt(ctx.tryCounter as int64));
|
||||||
ctx.tryCounter = ctx.tryCounter + 1;
|
ctx.tryCounter = ctx.tryCounter + 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user