2e536488e6
- Add tekFunc AST node and parser support for func(Params) -> Ret - Add tkFunc type resolution in sema and hir_lower - Add &FuncName address-taking yielding tkMutRef(tkFunc) - Fix C emission: function pointer params use Ret (*name)(Params) syntax via cParamDecl helper that embeds the name inside (*) - Fix forward declarations and temp declarations for function pointers - Add funcPtrTypes table in C backend so &Fn temps get proper type - Add _test_funcptr integration test
237 lines
8.0 KiB
Nim
237 lines
8.0 KiB
Nim
import std/[sequtils, strutils]
|
|
import token
|
|
|
|
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
|
|
# &mut func(...) -> func(...) (function pointer decay)
|
|
if a.isMutRef and b.kind == tkFunc:
|
|
if a.inner.len > 0 and a.inner[0].kind == tkFunc:
|
|
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
|