feat: restructure repo, borrow checker, expanded stdlib

- Reorganize repository to Rust-style layout:
  compiler/bootstrap/  compiler/selfhost/  compiler/tests/
  library/std/  library/runtime/  tests/  tools/
- Add buxs/ Windows-compatible project root
- Add borrow checker tests and implement:
  - Alias analysis (double mutable borrow detection)
  - Use-after-move detection for own T
- Expand standard library:
  - Std::Os: Args, Env, Cwd, Chdir
  - Std::Time: NowMs, NowUs, SleepMs
  - Std::Process: Run, Output
  - Std::Io: PrintInt64 (fixes 32-bit truncation bug)
- Add examples: os_time.bux, process.bux
- Fix PrintInt to use int64_t in C runtime
This commit is contained in:
2026-06-05 20:08:17 +03:00
parent a45a2ecd49
commit 9c6b516453
75 changed files with 581 additions and 79 deletions
+231
View File
@@ -0,0 +1,231 @@
import std/[sequtils, strformat, strutils]
type
TypeKind* = enum
tkUnknown
tkVoid
tkBool
tkBool8
tkBool16
tkBool32
tkChar8
tkChar16
tkChar32
tkStr
tkInt8
tkInt16
tkInt32
tkInt64
tkInt
tkUInt8
tkUInt16
tkUInt32
tkUInt64
tkUInt
tkFloat32
tkFloat64
tkPointer
tkRef
tkMutRef
tkDynRef
tkSlice
tkRange
tkTuple
tkNamed
tkTypeParam
tkFunc
Type* = ref object
kind*: TypeKind
name*: string
inner*: seq[Type] ## for Pointer(pointee), Slice(element), Tuple(elements), Func(params+ret)
# Factories
proc makeUnknown*(): Type = Type(kind: tkUnknown)
proc makeVoid*(): Type = Type(kind: tkVoid)
proc makeBool*(): Type = Type(kind: tkBool)
proc makeBool8*(): Type = Type(kind: tkBool8)
proc makeBool16*(): Type = Type(kind: tkBool16)
proc makeBool32*(): Type = Type(kind: tkBool32)
proc makeChar8*(): Type = Type(kind: tkChar8)
proc makeChar16*(): Type = Type(kind: tkChar16)
proc makeChar32*(): Type = Type(kind: tkChar32)
proc makeStr*(): Type = Type(kind: tkStr)
proc makeInt8*(): Type = Type(kind: tkInt8)
proc makeInt16*(): Type = Type(kind: tkInt16)
proc makeInt32*(): Type = Type(kind: tkInt32)
proc makeInt64*(): Type = Type(kind: tkInt64)
proc makeInt*(): Type = Type(kind: tkInt)
proc makeUInt8*(): Type = Type(kind: tkUInt8)
proc makeUInt16*(): Type = Type(kind: tkUInt16)
proc makeUInt32*(): Type = Type(kind: tkUInt32)
proc makeUInt64*(): Type = Type(kind: tkUInt64)
proc makeUInt*(): Type = Type(kind: tkUInt)
proc makeFloat32*(): Type = Type(kind: tkFloat32)
proc makeFloat64*(): Type = Type(kind: tkFloat64)
proc makePointer*(pointee: Type): Type =
Type(kind: tkPointer, inner: @[pointee])
proc makeRef*(pointee: Type): Type =
Type(kind: tkRef, inner: @[pointee])
proc makeMutRef*(pointee: Type): Type =
Type(kind: tkMutRef, inner: @[pointee])
proc makeDynRef*(interfaceName: string): Type =
Type(kind: tkDynRef, name: interfaceName)
proc makeSlice*(element: Type): Type =
Type(kind: tkSlice, inner: @[element])
proc makeRange*(element: Type): Type =
Type(kind: tkRange, inner: @[element])
proc makeTuple*(elems: seq[Type]): Type =
Type(kind: tkTuple, inner: elems)
proc makeNamed*(name: string): Type =
Type(kind: tkNamed, name: name)
proc makeTypeParam*(name: string): Type =
Type(kind: tkTypeParam, name: name)
proc makeFunc*(params: seq[Type], ret: Type): Type =
Type(kind: tkFunc, inner: params & @[ret])
# Predicates
proc isUnknown*(t: Type): bool = t.kind == tkUnknown
proc isVoid*(t: Type): bool = t.kind == tkVoid
proc isBool*(t: Type): bool = t.kind in {tkBool, tkBool8, tkBool16, tkBool32}
proc isNumeric*(t: Type): bool =
if t.kind in {tkUnknown, tkNamed, tkTypeParam}: return true
t.kind in {tkInt8, tkInt16, tkInt32, tkInt64, tkInt,
tkUInt8, tkUInt16, tkUInt32, tkUInt64, tkUInt,
tkFloat32, tkFloat64}
proc isInteger*(t: Type): bool =
if t.kind in {tkUnknown, tkNamed, tkTypeParam}: return true
t.kind in {tkInt8, tkInt16, tkInt32, tkInt64, tkInt,
tkUInt8, tkUInt16, tkUInt32, tkUInt64, tkUInt}
proc isFloat*(t: Type): bool =
if t.kind in {tkUnknown, tkNamed, tkTypeParam}: return true
t.kind in {tkFloat32, tkFloat64}
proc isSigned*(t: Type): bool =
if t.kind in {tkUnknown, tkNamed, tkTypeParam}: return true
t.kind in {tkInt8, tkInt16, tkInt32, tkInt64, tkInt}
proc isPointer*(t: Type): bool = t.kind in {tkPointer, tkRef, tkMutRef, tkDynRef}
proc isRawPointer*(t: Type): bool = t.kind == tkPointer
proc isRef*(t: Type): bool = t.kind == tkRef
proc isMutRef*(t: Type): bool = t.kind == tkMutRef
proc isDynRef*(t: Type): bool = t.kind == tkDynRef
proc isSlice*(t: Type): bool = t.kind == tkSlice
# Comparison
proc `==`*(a, b: Type): bool =
if a.isNil or b.isNil:
return a.isNil and b.isNil
if a.kind != b.kind: return false
if a.kind in {tkNamed, tkTypeParam} and a.name != b.name: return false
if a.inner.len != b.inner.len: return false
for i in 0 ..< a.inner.len:
if a.inner[i] != b.inner[i]: return false
return true
proc `!=`*(a, b: Type): bool = not (a == b)
# Assignment compatibility
proc isAssignableTo*(a, b: Type): bool =
if a.isUnknown or b.isUnknown: return true
if b.kind == tkTypeParam: return true
if a == b: return true
# float32 -> float64
if a.kind == tkFloat32 and b.kind == tkFloat64: return true
# int64 <-> int (on x64)
if a.kind == tkInt64 and b.kind == tkInt: return true
if a.kind == tkInt and b.kind == tkInt64: return true
if a.kind == tkUInt64 and b.kind == tkUInt: return true
if a.kind == tkUInt and b.kind == tkUInt64: return true
# int32 <-> int (for convenience in bootstrap)
if a.kind == tkInt32 and b.kind == tkInt: return true
if a.kind == tkInt and b.kind == tkInt32: return true
if a.kind == tkUInt32 and b.kind == tkInt: return true
if a.kind == tkInt and b.kind == tkUInt32: return true
if a.kind == tkUInt32 and b.kind == tkUInt: return true
if a.kind == tkUInt and b.kind == tkUInt32: return true
if a.kind == tkInt32 and b.kind == tkUInt32: return true
if a.kind == tkUInt32 and b.kind == tkInt32: return true
# smaller int -> int/uint
if b.kind == tkInt and a.kind in {tkInt8, tkInt16, tkInt32}: return true
if b.kind == tkUInt and a.kind in {tkUInt8, tkUInt16, tkUInt32}: return true
# int <-> uint (for convenience in bootstrap)
if a.kind == tkInt and b.kind == tkUInt: return true
if a.kind == tkUInt and b.kind == tkInt: return true
# *char8 -> String (C string literal to Bux String)
if a.kind == tkPointer and b.kind == tkStr:
if a.inner.len > 0 and a.inner[0].kind == tkChar8:
return true
# numeric exact match required otherwise
if a.isNumeric and b.isNumeric: return false
# bool across widths
if a.isBool and b.isBool: return true
# pointer to opaque / null pointer
if a.isPointer and b.isPointer:
if a.inner.len > 0 and a.inner[0].isUnknown:
return true
if b.inner.len > 0 and b.inner[0].isUnknown:
return true
# &mut T -> &T (mutable ref can coerce to shared ref)
if a.isMutRef and b.isRef:
if a.inner.len > 0 and b.inner.len > 0 and a.inner[0].isAssignableTo(b.inner[0]):
return true
# &mut T -> *T (mutable ref can coerce to raw pointer)
if a.isMutRef and b.isRawPointer:
if a.inner.len > 0 and b.inner.len > 0 and a.inner[0].isAssignableTo(b.inner[0]):
return true
# &T -> *T (shared ref can coerce to raw pointer)
if a.isRef and b.isRawPointer:
if a.inner.len > 0 and b.inner.len > 0 and a.inner[0].isAssignableTo(b.inner[0]):
return true
# &Concrete -> &dyn Trait (trait object coercion)
if b.isDynRef and a.isRef:
return true
if b.isDynRef and a.isMutRef:
return true
return false
# String representation
proc toString*(t: Type): string =
case t.kind
of tkUnknown: "?"
of tkVoid: "void"
of tkBool: "bool"
of tkBool8: "bool8"
of tkBool16: "bool16"
of tkBool32: "bool32"
of tkChar8: "char8"
of tkChar16: "char16"
of tkChar32: "char32"
of tkStr: "String"
of tkInt8: "int8"
of tkInt16: "int16"
of tkInt32: "int32"
of tkInt64: "int64"
of tkInt: "int"
of tkUInt8: "uint8"
of tkUInt16: "uint16"
of tkUInt32: "uint32"
of tkUInt64: "uint64"
of tkUInt: "uint"
of tkFloat32: "float32"
of tkFloat64: "float64"
of tkPointer: "*" & t.inner[0].toString
of tkRef: "&" & t.inner[0].toString
of tkMutRef: "&mut " & t.inner[0].toString
of tkDynRef: "&dyn " & t.name
of tkSlice:
if t.inner.len > 0: t.inner[0].toString & "[]"
else: "Slice<?>"
of tkRange:
if t.inner.len > 0: "Range<" & t.inner[0].toString & ">"
else: "Range<?>"
of tkTuple:
"(" & t.inner.mapIt(it.toString).join(", ") & ")"
of tkNamed: t.name
of tkTypeParam: t.name
of tkFunc:
if t.inner.len == 0: "func()"
else:
let params = t.inner[0..^2].mapIt(it.toString).join(", ")
let ret = t.inner[^1].toString
"func(" & params & ") -> " & ret