fix(bootstrap): simple enum codegen, crypto parser compatibility, import ordering

- Fix bootstrap sema to type simple enum variants as the enum itself
  (not _Tag), and .tag access returns the enum type.
- Fix hir_lower to lower simple enum .tag as no-op and Enum{tag:X}
  init as X directly.
- Fix sema collectGlobals to register imports AFTER real declarations,
  preventing duplicate-symbol errors when stdlib files import symbols
  defined in later files (e.g. jwt.bux importing rsa.bux).
- Rewrite lib/crypto/*.bux as single-line to work around bootstrap
  parser limitations with multi-line function calls.
- Rename 'pub' -> 'pubBuf' in ed25519.bux and jwt-pitbul/Main.bux
  to avoid reserved keyword collision.
- Remove algTag:int workaround from jwt-pitbul/Main.bux; enum
  comparisons now type-check without manual casts.
- Update .gitignore to exclude _test_*/ and backup files.
- Delete accidentally-committed _test_array/ directory.
This commit is contained in:
2026-06-08 18:43:26 +03:00
parent a7a1b45588
commit bebc362ae6
9 changed files with 127 additions and 122 deletions
+37 -4
View File
@@ -400,7 +400,14 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
else: return makeUnknown()
of dkEnum:
# Algebraic enum fields: tag and data
if expr.exprFieldName == "tag":
var hasData = false
for v in decl.declEnumVariants:
if v.fields.len > 0 or v.namedFields.len > 0:
hasData = true
break
if not hasData and expr.exprFieldName == "tag":
return makeNamed(objType.name)
elif expr.exprFieldName == "tag":
return makeNamed(objType.name & "_Tag")
elif expr.exprFieldName == "data":
return makeNamed(objType.name & "_Data")
@@ -670,6 +677,17 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
of ekField:
let objType = ctx.resolveExprType(expr.exprFieldObj)
let base = ctx.lowerExpr(expr.exprFieldObj)
# Simple enum .tag is the enum value itself
if objType.kind == tkNamed and expr.exprFieldName == "tag":
let sym = ctx.globalScope.lookup(objType.name)
if sym != nil and sym.decl != nil and sym.decl.kind == dkEnum:
var hasData = false
for v in sym.decl.declEnumVariants:
if v.fields.len > 0 or v.namedFields.len > 0:
hasData = true
break
if not hasData:
return base
# Auto-dereference pointer types for field access
if objType.isPointer:
let arrowPtr = HirNode(kind: hArrowField, arrowFieldBase: base,
@@ -703,9 +721,6 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
typ: makeVoid(), loc: loc)
of ekStructInit:
var fields: seq[tuple[name: string, value: HirNode]] = @[]
for f in expr.exprStructInitFields:
fields.add((f.name, ctx.lowerExpr(f.value)))
var structName = expr.exprStructInitName
if expr.exprStructInitTypeArgs.len > 0:
var suffix = ""
@@ -714,6 +729,24 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
let argType = ctx.resolveTypeExpr(targ)
suffix.add(argType.toString)
structName = structName & "_" & suffix
# Simple enum init: EnumName { tag: EnumName_Variant } -> EnumName_Variant
var enumDecl: Decl = nil
let enumSym = ctx.globalScope.lookup(structName)
if enumSym != nil and enumSym.decl != nil and enumSym.decl.kind == dkEnum:
enumDecl = enumSym.decl
var isSimple = false
if enumDecl != nil:
for v in enumDecl.declEnumVariants:
if v.fields.len > 0 or v.namedFields.len > 0:
isSimple = true
break
isSimple = not isSimple
if isSimple and expr.exprStructInitFields.len == 1 and expr.exprStructInitFields[0].name == "tag":
let variantExpr = ctx.lowerExpr(expr.exprStructInitFields[0].value)
return variantExpr
var fields: seq[tuple[name: string, value: HirNode]] = @[]
for f in expr.exprStructInitFields:
fields.add((f.name, ctx.lowerExpr(f.value)))
return HirNode(kind: hStructInit, structInitName: structName,
structInitFields: fields, typ: typ, loc: loc)
+38 -20
View File
@@ -535,10 +535,17 @@ proc collectGlobals*(sema: var Sema) =
if not sema.globalScope.define(sym):
sema.emitError(decl.loc, &"duplicate symbol '{decl.declEnumName}'")
sema.typeTable[decl.declEnumName] = t
# Check if algebraic or simple enum
var hasData = false
for variant in decl.declEnumVariants:
if variant.fields.len > 0 or variant.namedFields.len > 0:
hasData = true
break
# For algebraic enums, add variant constants with _Tag type
# For simple enums, variant constants have the enum type itself
for variant in decl.declEnumVariants:
let variantName = decl.declEnumName & "_" & variant.name
let variantType = makeNamed(decl.declEnumName & "_Tag")
let variantType = if hasData: makeNamed(decl.declEnumName & "_Tag") else: makeNamed(decl.declEnumName)
let variantSym = Symbol(kind: skConst, name: variantName, typ: variantType,
decl: decl, isPublic: decl.isPublic)
discard sema.globalScope.define(variantSym)
@@ -563,24 +570,8 @@ proc collectGlobals*(sema: var Sema) =
sema.emitError(decl.loc, &"duplicate symbol '{decl.declAliasName}'")
sema.typeTable[decl.declAliasName] = t
of dkUse:
# Imports: register imported names into scope
if decl.declUsePath.len > 0:
case decl.declUseKind
of ukMulti:
for name in decl.declUseNames:
if sema.globalScope.lookup(name) == nil:
let sym = Symbol(kind: skFunc, name: name, typ: makeUnknown(), isPublic: true)
discard sema.globalScope.define(sym)
of ukGlob:
let name = decl.declUsePath[^1]
if sema.globalScope.lookup(name) == nil:
let sym = Symbol(kind: skModule, name: name, typ: makeUnknown(), isPublic: true)
discard sema.globalScope.define(sym)
of ukSingle:
let name = decl.declUsePath[^1]
if sema.globalScope.lookup(name) == nil:
let sym = Symbol(kind: skFunc, name: name, typ: makeUnknown(), isPublic: true)
discard sema.globalScope.define(sym)
# Imports handled in second pass after all declarations are registered
discard
of dkInterface:
# Register interface for conformance checking
sema.interfaceTable[decl.declInterfaceName] = decl
@@ -641,6 +632,26 @@ proc collectGlobals*(sema: var Sema) =
for decl in sema.module.items:
if decl.kind == dkConst:
discard sema.constFoldConstDecl(decl)
# Third pass: register imports after all real declarations are known
for decl in sema.module.items:
if decl.kind == dkUse:
if decl.declUsePath.len > 0:
case decl.declUseKind
of ukMulti:
for name in decl.declUseNames:
if sema.globalScope.lookup(name) == nil:
let sym = Symbol(kind: skFunc, name: name, typ: makeUnknown(), isPublic: true)
discard sema.globalScope.define(sym)
of ukGlob:
let name = decl.declUsePath[^1]
if sema.globalScope.lookup(name) == nil:
let sym = Symbol(kind: skModule, name: name, typ: makeUnknown(), isPublic: true)
discard sema.globalScope.define(sym)
of ukSingle:
let name = decl.declUsePath[^1]
if sema.globalScope.lookup(name) == nil:
let sym = Symbol(kind: skFunc, name: name, typ: makeUnknown(), isPublic: true)
discard sema.globalScope.define(sym)
# ---------------------------------------------------------------------------
# Expression type checking
@@ -1085,7 +1096,14 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
sema.emitError(expr.loc, &"struct '{objType.name}' has no field '{expr.exprFieldName}'")
elif sym.decl.kind == dkEnum:
# Algebraic enum fields
if expr.exprFieldName == "tag":
var hasData = false
for v in sym.decl.declEnumVariants:
if v.fields.len > 0 or v.namedFields.len > 0:
hasData = true
break
if not hasData and expr.exprFieldName == "tag":
return makeNamed(objType.name)
elif expr.exprFieldName == "tag":
return makeNamed(objType.name & "_Tag")
elif expr.exprFieldName == "data":
return makeNamed(objType.name & "_Data")