Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
This commit is contained in:
commit
5aac789e0e
53 changed files with 1264 additions and 329 deletions
|
|
@ -188,6 +188,10 @@ type
|
||||||
nkStmtListType, # a statement list ending in a type; for macros
|
nkStmtListType, # a statement list ending in a type; for macros
|
||||||
nkBlockType, # a statement block ending in a type; for macros
|
nkBlockType, # a statement block ending in a type; for macros
|
||||||
# types as syntactic trees:
|
# types as syntactic trees:
|
||||||
|
|
||||||
|
nkWith, # distinct with `foo`
|
||||||
|
nkWithout, # distinct without `foo`
|
||||||
|
|
||||||
nkTypeOfExpr, # type(1+2)
|
nkTypeOfExpr, # type(1+2)
|
||||||
nkObjectTy, # object body
|
nkObjectTy, # object body
|
||||||
nkTupleTy, # tuple body
|
nkTupleTy, # tuple body
|
||||||
|
|
@ -383,6 +387,10 @@ type
|
||||||
# sons[1]: field type
|
# sons[1]: field type
|
||||||
# .n: nkDotExpr storing the field name
|
# .n: nkDotExpr storing the field name
|
||||||
|
|
||||||
|
static:
|
||||||
|
# remind us when TTypeKind stops to fit in a single 64-bit word
|
||||||
|
assert TTypeKind.high.ord <= 63
|
||||||
|
|
||||||
const
|
const
|
||||||
tyPureObject* = tyTuple
|
tyPureObject* = tyTuple
|
||||||
GcTypeKinds* = {tyRef, tySequence, tyString}
|
GcTypeKinds* = {tyRef, tySequence, tyString}
|
||||||
|
|
@ -394,7 +402,7 @@ const
|
||||||
tyUserTypeClass, tyUserTypeClassInst,
|
tyUserTypeClass, tyUserTypeClassInst,
|
||||||
tyAnd, tyOr, tyNot, tyAnything}
|
tyAnd, tyOr, tyNot, tyAnything}
|
||||||
|
|
||||||
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses
|
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses
|
||||||
|
|
||||||
type
|
type
|
||||||
TTypeKinds* = set[TTypeKind]
|
TTypeKinds* = set[TTypeKind]
|
||||||
|
|
@ -428,8 +436,11 @@ type
|
||||||
tfFromGeneric, # type is an instantiation of a generic; this is needed
|
tfFromGeneric, # type is an instantiation of a generic; this is needed
|
||||||
# because for instantiations of objects, structural
|
# because for instantiations of objects, structural
|
||||||
# type equality has to be used
|
# type equality has to be used
|
||||||
tfUnresolved, # marks unresolved typedesc params: e.g.
|
tfUnresolved, # marks unresolved typedesc/static params: e.g.
|
||||||
# proc foo(T: typedesc, list: seq[T]): var T
|
# proc foo(T: typedesc, list: seq[T]): var T
|
||||||
|
# proc foo(L: static[int]): array[L, int]
|
||||||
|
# can be attached to ranges to indicate that the range
|
||||||
|
# depends on unresolved static params.
|
||||||
tfRetType, # marks return types in proc (used to detect type classes
|
tfRetType, # marks return types in proc (used to detect type classes
|
||||||
# used as return types for return type inference)
|
# used as return types for return type inference)
|
||||||
tfCapturesEnv, # whether proc really captures some environment
|
tfCapturesEnv, # whether proc really captures some environment
|
||||||
|
|
@ -445,9 +456,14 @@ type
|
||||||
tfHasMeta, # type contains "wildcard" sub-types such as generic params
|
tfHasMeta, # type contains "wildcard" sub-types such as generic params
|
||||||
# or other type classes
|
# or other type classes
|
||||||
tfHasGCedMem, # type contains GC'ed memory
|
tfHasGCedMem, # type contains GC'ed memory
|
||||||
|
tfPacked
|
||||||
tfHasStatic
|
tfHasStatic
|
||||||
tfGenericTypeParam
|
tfGenericTypeParam
|
||||||
tfImplicitTypeParam
|
tfImplicitTypeParam
|
||||||
|
tfWildcard # consider a proc like foo[T, I](x: Type[T, I])
|
||||||
|
# T and I here can bind to both typedesc and static types
|
||||||
|
# before this is determined, we'll consider them to be a
|
||||||
|
# wildcard type.
|
||||||
|
|
||||||
TTypeFlags* = set[TTypeFlag]
|
TTypeFlags* = set[TTypeFlag]
|
||||||
|
|
||||||
|
|
@ -693,7 +709,7 @@ type
|
||||||
TSym* {.acyclic.} = object of TIdObj
|
TSym* {.acyclic.} = object of TIdObj
|
||||||
# proc and type instantiations are cached in the generic symbol
|
# proc and type instantiations are cached in the generic symbol
|
||||||
case kind*: TSymKind
|
case kind*: TSymKind
|
||||||
of skType:
|
of skType, skGenericParam:
|
||||||
typeInstCache*: seq[PType]
|
typeInstCache*: seq[PType]
|
||||||
typScope*: PScope
|
typScope*: PScope
|
||||||
of routineKinds:
|
of routineKinds:
|
||||||
|
|
@ -961,7 +977,9 @@ var emptyNode* = newNode(nkEmpty)
|
||||||
# There is a single empty node that is shared! Do not overwrite it!
|
# There is a single empty node that is shared! Do not overwrite it!
|
||||||
|
|
||||||
proc isMetaType*(t: PType): bool =
|
proc isMetaType*(t: PType): bool =
|
||||||
return t.kind in tyMetaTypes or tfHasMeta in t.flags
|
return t.kind in tyMetaTypes or
|
||||||
|
(t.kind == tyStatic and t.n == nil) or
|
||||||
|
tfHasMeta in t.flags
|
||||||
|
|
||||||
proc linkTo*(t: PType, s: PSym): PType {.discardable.} =
|
proc linkTo*(t: PType, s: PSym): PType {.discardable.} =
|
||||||
t.sym = s
|
t.sym = s
|
||||||
|
|
@ -1305,7 +1323,7 @@ proc propagateToOwner*(owner, elem: PType) =
|
||||||
if tfShared in elem.flags:
|
if tfShared in elem.flags:
|
||||||
owner.flags.incl tfHasShared
|
owner.flags.incl tfHasShared
|
||||||
|
|
||||||
if elem.kind in tyMetaTypes:
|
if elem.isMetaType:
|
||||||
owner.flags.incl tfHasMeta
|
owner.flags.incl tfHasMeta
|
||||||
|
|
||||||
if elem.kind in {tyString, tyRef, tySequence} or
|
if elem.kind in {tyString, tyRef, tySequence} or
|
||||||
|
|
@ -1495,6 +1513,9 @@ proc hasPattern*(s: PSym): bool {.inline.} =
|
||||||
iterator items*(n: PNode): PNode =
|
iterator items*(n: PNode): PNode =
|
||||||
for i in 0.. <n.len: yield n.sons[i]
|
for i in 0.. <n.len: yield n.sons[i]
|
||||||
|
|
||||||
|
iterator pairs*(n: PNode): tuple[i: int, n: PNode] =
|
||||||
|
for i in 0.. <n.len: yield (i, n.sons[i])
|
||||||
|
|
||||||
proc isAtom*(n: PNode): bool {.inline.} =
|
proc isAtom*(n: PNode): bool {.inline.} =
|
||||||
result = n.kind >= nkNone and n.kind <= nkNilLit
|
result = n.kind >= nkNone and n.kind <= nkNilLit
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -464,24 +464,33 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
|
||||||
check: var TIntSet): PRope =
|
check: var TIntSet): PRope =
|
||||||
# declare the record:
|
# declare the record:
|
||||||
var hasField = false
|
var hasField = false
|
||||||
let aStruct = structOrUnion(typ)
|
|
||||||
|
var attribute: PRope =
|
||||||
|
if tfPacked in typ.flags: toRope(CC[ccompiler].packedPragma)
|
||||||
|
else: nil
|
||||||
|
|
||||||
|
result = ropecg(m, CC[ccompiler].structStmtFmt,
|
||||||
|
[structOrUnion(typ), name, attribute])
|
||||||
|
|
||||||
if typ.kind == tyObject:
|
if typ.kind == tyObject:
|
||||||
|
|
||||||
if typ.sons[0] == nil:
|
if typ.sons[0] == nil:
|
||||||
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
|
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
|
||||||
result = ropecg(m, "$1 $2 {$n", [aStruct, name])
|
appcg(m, result, " {$n", [])
|
||||||
else:
|
else:
|
||||||
result = ropecg(m, "$1 $2 {$n#TNimType* m_type;$n", [aStruct, name])
|
appcg(m, result, " {$n#TNimType* m_type;$n", [name, attribute])
|
||||||
hasField = true
|
hasField = true
|
||||||
elif gCmd == cmdCompileToCpp:
|
elif gCmd == cmdCompileToCpp:
|
||||||
result = ropecg(m, "$1 $2 : public $3 {$n",
|
appcg(m, result, " : public $1 {$n",
|
||||||
[aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
|
[getTypeDescAux(m, typ.sons[0], check)])
|
||||||
hasField = true
|
hasField = true
|
||||||
else:
|
else:
|
||||||
result = ropecg(m, "$1 $2 {$n $3 Sup;$n",
|
appcg(m, result, " {$n $1 Sup;$n",
|
||||||
[aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
|
[getTypeDescAux(m, typ.sons[0], check)])
|
||||||
hasField = true
|
hasField = true
|
||||||
else:
|
else:
|
||||||
result = ropef("$1 $2 {$n", [aStruct, name])
|
appf(result, " {$n", [name])
|
||||||
|
|
||||||
var desc = getRecordFields(m, typ, check)
|
var desc = getRecordFields(m, typ, check)
|
||||||
if (desc == nil) and not hasField:
|
if (desc == nil) and not hasField:
|
||||||
appf(result, "char dummy;$n", [])
|
appf(result, "char dummy;$n", [])
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,10 @@ proc getUniqueType*(key: PType): PType =
|
||||||
gCanonicalTypes[k] = key
|
gCanonicalTypes[k] = key
|
||||||
result = key
|
result = key
|
||||||
of tyTypeDesc, tyTypeClasses, tyGenericParam,
|
of tyTypeDesc, tyTypeClasses, tyGenericParam,
|
||||||
tyFromExpr, tyStatic, tyFieldAccessor:
|
tyFromExpr, tyFieldAccessor:
|
||||||
internalError("GetUniqueType")
|
internalError("GetUniqueType")
|
||||||
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter:
|
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable,
|
||||||
|
tyConst, tyIter, tyStatic:
|
||||||
result = getUniqueType(lastSon(key))
|
result = getUniqueType(lastSon(key))
|
||||||
of tyArrayConstr, tyGenericInvokation, tyGenericBody,
|
of tyArrayConstr, tyGenericInvokation, tyGenericBody,
|
||||||
tyOpenArray, tyArray, tySet, tyRange, tyTuple,
|
tyOpenArray, tyArray, tySet, tyRange, tyTuple,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import
|
||||||
type
|
type
|
||||||
TSystemCC* = enum
|
TSystemCC* = enum
|
||||||
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
||||||
ccTcc, ccPcc, ccUcc, ccIcl, ccGpp
|
ccTcc, ccPcc, ccUcc, ccIcl
|
||||||
TInfoCCProp* = enum # properties of the C compiler:
|
TInfoCCProp* = enum # properties of the C compiler:
|
||||||
hasSwitchRange, # CC allows ranges in switch statements (GNU C)
|
hasSwitchRange, # CC allows ranges in switch statements (GNU C)
|
||||||
hasComputedGoto, # CC has computed goto (GNU C extension)
|
hasComputedGoto, # CC has computed goto (GNU C extension)
|
||||||
|
|
@ -33,11 +33,12 @@ type
|
||||||
optSpeed: string, # the options for optimization for speed
|
optSpeed: string, # the options for optimization for speed
|
||||||
optSize: string, # the options for optimization for size
|
optSize: string, # the options for optimization for size
|
||||||
compilerExe: string, # the compiler's executable
|
compilerExe: string, # the compiler's executable
|
||||||
|
cppCompiler: string, # name of the C++ compiler's executable (if supported)
|
||||||
compileTmpl: string, # the compile command template
|
compileTmpl: string, # the compile command template
|
||||||
buildGui: string, # command to build a GUI application
|
buildGui: string, # command to build a GUI application
|
||||||
buildDll: string, # command to build a shared library
|
buildDll: string, # command to build a shared library
|
||||||
buildLib: string, # command to build a static library
|
buildLib: string, # command to build a static library
|
||||||
linkerExe: string, # the linker's executable
|
linkerExe: string, # the linker's executable (if not matching compiler's)
|
||||||
linkTmpl: string, # command to link files to produce an exe
|
linkTmpl: string, # command to link files to produce an exe
|
||||||
includeCmd: string, # command to add an include dir
|
includeCmd: string, # command to add an include dir
|
||||||
linkDirCmd: string, # command to add a lib dir
|
linkDirCmd: string, # command to add a lib dir
|
||||||
|
|
@ -46,6 +47,8 @@ type
|
||||||
pic: string, # command for position independent code
|
pic: string, # command for position independent code
|
||||||
# used on some platforms
|
# used on some platforms
|
||||||
asmStmtFrmt: string, # format of ASM statement
|
asmStmtFrmt: string, # format of ASM statement
|
||||||
|
structStmtFmt: string, # Format for struct statement
|
||||||
|
packedPragma: string, # Attribute/pragma to make struct packed (1-byte aligned)
|
||||||
props: TInfoCCProps] # properties of the C compiler
|
props: TInfoCCProps] # properties of the C compiler
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -63,11 +66,12 @@ compiler gcc:
|
||||||
optSpeed: " -O3 -ffast-math ",
|
optSpeed: " -O3 -ffast-math ",
|
||||||
optSize: " -Os -ffast-math ",
|
optSize: " -Os -ffast-math ",
|
||||||
compilerExe: "gcc",
|
compilerExe: "gcc",
|
||||||
|
cppCompiler: "g++",
|
||||||
compileTmpl: "-c $options $include -o $objfile $file",
|
compileTmpl: "-c $options $include -o $objfile $file",
|
||||||
buildGui: " -mwindows",
|
buildGui: " -mwindows",
|
||||||
buildDll: " -shared",
|
buildDll: " -shared",
|
||||||
buildLib: "ar rcs $libfile $objfiles",
|
buildLib: "ar rcs $libfile $objfiles",
|
||||||
linkerExe: "gcc",
|
linkerExe: "",
|
||||||
linkTmpl: "$buildgui $builddll -o $exefile $objfiles $options",
|
linkTmpl: "$buildgui $builddll -o $exefile $objfiles $options",
|
||||||
includeCmd: " -I",
|
includeCmd: " -I",
|
||||||
linkDirCmd: " -L",
|
linkDirCmd: " -L",
|
||||||
|
|
@ -75,34 +79,25 @@ compiler gcc:
|
||||||
debug: "",
|
debug: "",
|
||||||
pic: "-fPIC",
|
pic: "-fPIC",
|
||||||
asmStmtFrmt: "asm($1);$n",
|
asmStmtFrmt: "asm($1);$n",
|
||||||
|
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
|
||||||
|
packedPragma: "__attribute__((__packed__))",
|
||||||
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
||||||
hasNakedAttribute})
|
hasNakedAttribute})
|
||||||
|
|
||||||
compiler gpp:
|
|
||||||
result = gcc()
|
|
||||||
|
|
||||||
result.name = "gpp"
|
|
||||||
result.compilerExe = "g++"
|
|
||||||
result.linkerExe = "g++"
|
|
||||||
|
|
||||||
result.buildDll = " -mdll"
|
|
||||||
# XXX: Hmm, I'm keeping this from the previos version,
|
|
||||||
# but my gcc doesn't even have such an option (is this mingw?)
|
|
||||||
|
|
||||||
compiler llvmGcc:
|
compiler llvmGcc:
|
||||||
result = gcc()
|
result = gcc()
|
||||||
|
|
||||||
result.name = "llvm_gcc"
|
result.name = "llvm_gcc"
|
||||||
result.compilerExe = "llvm-gcc"
|
result.compilerExe = "llvm-gcc"
|
||||||
|
result.cppCompiler = "llvm-g++"
|
||||||
result.buildLib = "llvm-ar rcs $libfile $objfiles"
|
result.buildLib = "llvm-ar rcs $libfile $objfiles"
|
||||||
result.linkerExe = "llvm-gcc"
|
|
||||||
|
|
||||||
compiler clang:
|
compiler clang:
|
||||||
result = llvmGcc()
|
result = llvmGcc()
|
||||||
|
|
||||||
result.name = "clang"
|
result.name = "clang"
|
||||||
result.compilerExe = "clang"
|
result.compilerExe = "clang"
|
||||||
result.linkerExe = "clang"
|
result.cppCompiler = "clang++"
|
||||||
|
|
||||||
compiler vcc:
|
compiler vcc:
|
||||||
result = (
|
result = (
|
||||||
|
|
@ -111,6 +106,7 @@ compiler vcc:
|
||||||
optSpeed: " /Ogityb2 /G7 /arch:SSE2 ",
|
optSpeed: " /Ogityb2 /G7 /arch:SSE2 ",
|
||||||
optSize: " /O1 /G7 ",
|
optSize: " /O1 /G7 ",
|
||||||
compilerExe: "cl",
|
compilerExe: "cl",
|
||||||
|
cppCompiler: "cl",
|
||||||
compileTmpl: "/c $options $include /Fo$objfile $file",
|
compileTmpl: "/c $options $include /Fo$objfile $file",
|
||||||
buildGui: " /link /SUBSYSTEM:WINDOWS ",
|
buildGui: " /link /SUBSYSTEM:WINDOWS ",
|
||||||
buildDll: " /LD",
|
buildDll: " /LD",
|
||||||
|
|
@ -123,6 +119,8 @@ compiler vcc:
|
||||||
debug: " /GZ /Zi ",
|
debug: " /GZ /Zi ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$3$n$1 $2",
|
||||||
|
packedPragma: "#pragma pack(1)",
|
||||||
props: {hasCpp, hasAssume, hasNakedDeclspec})
|
props: {hasCpp, hasAssume, hasNakedDeclspec})
|
||||||
|
|
||||||
compiler icl:
|
compiler icl:
|
||||||
|
|
@ -143,6 +141,7 @@ compiler lcc:
|
||||||
optSpeed: " -O -p6 ",
|
optSpeed: " -O -p6 ",
|
||||||
optSize: " -O -p6 ",
|
optSize: " -O -p6 ",
|
||||||
compilerExe: "lcc",
|
compilerExe: "lcc",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "$options $include -Fo$objfile $file",
|
compileTmpl: "$options $include -Fo$objfile $file",
|
||||||
buildGui: " -subsystem windows",
|
buildGui: " -subsystem windows",
|
||||||
buildDll: " -dll",
|
buildDll: " -dll",
|
||||||
|
|
@ -155,6 +154,8 @@ compiler lcc:
|
||||||
debug: " -g5 ",
|
debug: " -g5 ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "_asm{$n$1$n}$n",
|
asmStmtFrmt: "_asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {})
|
props: {})
|
||||||
|
|
||||||
compiler bcc:
|
compiler bcc:
|
||||||
|
|
@ -164,6 +165,7 @@ compiler bcc:
|
||||||
optSpeed: " -O2 -6 ",
|
optSpeed: " -O2 -6 ",
|
||||||
optSize: " -O1 -6 ",
|
optSize: " -O1 -6 ",
|
||||||
compilerExe: "bcc32",
|
compilerExe: "bcc32",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -o$objfile $file",
|
compileTmpl: "-c $options $include -o$objfile $file",
|
||||||
buildGui: " -tW",
|
buildGui: " -tW",
|
||||||
buildDll: " -tWD",
|
buildDll: " -tWD",
|
||||||
|
|
@ -176,6 +178,8 @@ compiler bcc:
|
||||||
debug: "",
|
debug: "",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {hasCpp})
|
props: {hasCpp})
|
||||||
|
|
||||||
compiler dmc:
|
compiler dmc:
|
||||||
|
|
@ -185,6 +189,7 @@ compiler dmc:
|
||||||
optSpeed: " -ff -o -6 ",
|
optSpeed: " -ff -o -6 ",
|
||||||
optSize: " -ff -o -6 ",
|
optSize: " -ff -o -6 ",
|
||||||
compilerExe: "dmc",
|
compilerExe: "dmc",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -o$objfile $file",
|
compileTmpl: "-c $options $include -o$objfile $file",
|
||||||
buildGui: " -L/exet:nt/su:windows",
|
buildGui: " -L/exet:nt/su:windows",
|
||||||
buildDll: " -WD",
|
buildDll: " -WD",
|
||||||
|
|
@ -197,6 +202,8 @@ compiler dmc:
|
||||||
debug: " -g ",
|
debug: " -g ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$3$n$1 $2",
|
||||||
|
packedPragma: "#pragma pack(1)",
|
||||||
props: {hasCpp})
|
props: {hasCpp})
|
||||||
|
|
||||||
compiler wcc:
|
compiler wcc:
|
||||||
|
|
@ -206,6 +213,7 @@ compiler wcc:
|
||||||
optSpeed: " -ox -on -6 -d0 -fp6 -zW ",
|
optSpeed: " -ox -on -6 -d0 -fp6 -zW ",
|
||||||
optSize: "",
|
optSize: "",
|
||||||
compilerExe: "wcl386",
|
compilerExe: "wcl386",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -fo=$objfile $file",
|
compileTmpl: "-c $options $include -fo=$objfile $file",
|
||||||
buildGui: " -bw",
|
buildGui: " -bw",
|
||||||
buildDll: " -bd",
|
buildDll: " -bd",
|
||||||
|
|
@ -218,6 +226,8 @@ compiler wcc:
|
||||||
debug: " -d2 ",
|
debug: " -d2 ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {hasCpp})
|
props: {hasCpp})
|
||||||
|
|
||||||
compiler tcc:
|
compiler tcc:
|
||||||
|
|
@ -227,6 +237,7 @@ compiler tcc:
|
||||||
optSpeed: "",
|
optSpeed: "",
|
||||||
optSize: "",
|
optSize: "",
|
||||||
compilerExe: "tcc",
|
compilerExe: "tcc",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -o $objfile $file",
|
compileTmpl: "-c $options $include -o $objfile $file",
|
||||||
buildGui: "UNAVAILABLE!",
|
buildGui: "UNAVAILABLE!",
|
||||||
buildDll: " -shared",
|
buildDll: " -shared",
|
||||||
|
|
@ -239,6 +250,8 @@ compiler tcc:
|
||||||
debug: " -g ",
|
debug: " -g ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {hasSwitchRange, hasComputedGoto})
|
props: {hasSwitchRange, hasComputedGoto})
|
||||||
|
|
||||||
compiler pcc:
|
compiler pcc:
|
||||||
|
|
@ -249,6 +262,7 @@ compiler pcc:
|
||||||
optSpeed: " -Ox ",
|
optSpeed: " -Ox ",
|
||||||
optSize: " -Os ",
|
optSize: " -Os ",
|
||||||
compilerExe: "cc",
|
compilerExe: "cc",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -Fo$objfile $file",
|
compileTmpl: "-c $options $include -Fo$objfile $file",
|
||||||
buildGui: " -SUBSYSTEM:WINDOWS",
|
buildGui: " -SUBSYSTEM:WINDOWS",
|
||||||
buildDll: " -DLL",
|
buildDll: " -DLL",
|
||||||
|
|
@ -261,6 +275,8 @@ compiler pcc:
|
||||||
debug: " -Zi ",
|
debug: " -Zi ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {})
|
props: {})
|
||||||
|
|
||||||
compiler ucc:
|
compiler ucc:
|
||||||
|
|
@ -270,6 +286,7 @@ compiler ucc:
|
||||||
optSpeed: " -O3 ",
|
optSpeed: " -O3 ",
|
||||||
optSize: " -O1 ",
|
optSize: " -O1 ",
|
||||||
compilerExe: "cc",
|
compilerExe: "cc",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -o $objfile $file",
|
compileTmpl: "-c $options $include -o $objfile $file",
|
||||||
buildGui: "",
|
buildGui: "",
|
||||||
buildDll: " -shared ",
|
buildDll: " -shared ",
|
||||||
|
|
@ -282,6 +299,8 @@ compiler ucc:
|
||||||
debug: "",
|
debug: "",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {})
|
props: {})
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -297,8 +316,7 @@ const
|
||||||
tcc(),
|
tcc(),
|
||||||
pcc(),
|
pcc(),
|
||||||
ucc(),
|
ucc(),
|
||||||
icl(),
|
icl()]
|
||||||
gpp()]
|
|
||||||
|
|
||||||
const
|
const
|
||||||
hExt* = ".h"
|
hExt* = ".h"
|
||||||
|
|
@ -471,11 +489,21 @@ proc needsExeExt(): bool {.inline.} =
|
||||||
result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
|
result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
|
||||||
(platform.hostOS == osWindows)
|
(platform.hostOS == osWindows)
|
||||||
|
|
||||||
|
proc getCompilerExe(compiler: TSystemCC): string =
|
||||||
|
result = if gCmd == cmdCompileToCpp: CC[compiler].cppCompiler
|
||||||
|
else: CC[compiler].compilerExe
|
||||||
|
if result.len == 0:
|
||||||
|
rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name)
|
||||||
|
|
||||||
|
proc getLinkerExe(compiler: TSystemCC): string =
|
||||||
|
result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
|
||||||
|
else: compiler.getCompilerExe
|
||||||
|
|
||||||
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
||||||
var c = cCompiler
|
var c = cCompiler
|
||||||
var options = cFileSpecificOptions(cfilename)
|
var options = cFileSpecificOptions(cfilename)
|
||||||
var exe = getConfigVar(c, ".exe")
|
var exe = getConfigVar(c, ".exe")
|
||||||
if exe.len == 0: exe = CC[c].compilerExe
|
if exe.len == 0: exe = c.getCompilerExe
|
||||||
|
|
||||||
if needsExeExt(): exe = addFileExt(exe, "exe")
|
if needsExeExt(): exe = addFileExt(exe, "exe")
|
||||||
if optGenDynLib in gGlobalOptions and
|
if optGenDynLib in gGlobalOptions and
|
||||||
|
|
@ -493,7 +521,7 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
||||||
compilePattern = joinPath(ccompilerpath, exe)
|
compilePattern = joinPath(ccompilerpath, exe)
|
||||||
else:
|
else:
|
||||||
includeCmd = ""
|
includeCmd = ""
|
||||||
compilePattern = CC[c].compilerExe
|
compilePattern = c.getCompilerExe
|
||||||
|
|
||||||
var cfile = if noAbsolutePaths(): extractFilename(cfilename)
|
var cfile = if noAbsolutePaths(): extractFilename(cfilename)
|
||||||
else: cfilename
|
else: cfilename
|
||||||
|
|
@ -600,7 +628,7 @@ proc callCCompiler*(projectfile: string) =
|
||||||
if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd)
|
if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd)
|
||||||
else:
|
else:
|
||||||
var linkerExe = getConfigVar(c, ".linkerexe")
|
var linkerExe = getConfigVar(c, ".linkerexe")
|
||||||
if len(linkerExe) == 0: linkerExe = CC[c].linkerExe
|
if len(linkerExe) == 0: linkerExe = c.getLinkerExe
|
||||||
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
|
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
|
||||||
if noAbsolutePaths(): linkCmd = quoteShell(linkerExe)
|
if noAbsolutePaths(): linkCmd = quoteShell(linkerExe)
|
||||||
else: linkCmd = quoteShell(joinPath(ccompilerpath, linkerExe))
|
else: linkCmd = quoteShell(joinPath(ccompilerpath, linkerExe))
|
||||||
|
|
|
||||||
|
|
@ -518,7 +518,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) =
|
||||||
else:
|
else:
|
||||||
internalError(it.info, "transformOuter")
|
internalError(it.info, "transformOuter")
|
||||||
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
|
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
|
||||||
nkClosure:
|
nkClosure, nkTypeSection:
|
||||||
# don't recurse here:
|
# don't recurse here:
|
||||||
# XXX recurse here and setup 'up' pointers
|
# XXX recurse here and setup 'up' pointers
|
||||||
discard
|
discard
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ type
|
||||||
base10, # base10 is listed as the first element,
|
base10, # base10 is listed as the first element,
|
||||||
# so that it is the correct default value
|
# so that it is the correct default value
|
||||||
base2, base8, base16
|
base2, base8, base16
|
||||||
|
|
||||||
TToken* = object # a Nimrod token
|
TToken* = object # a Nimrod token
|
||||||
tokType*: TTokType # the type of the token
|
tokType*: TTokType # the type of the token
|
||||||
indent*: int # the indentation; != -1 if the token has been
|
indent*: int # the indentation; != -1 if the token has been
|
||||||
|
|
@ -121,6 +122,7 @@ type
|
||||||
indentAhead*: int # if > 0 an indendation has already been read
|
indentAhead*: int # if > 0 an indendation has already been read
|
||||||
# this is needed because scanning comments
|
# this is needed because scanning comments
|
||||||
# needs so much look-ahead
|
# needs so much look-ahead
|
||||||
|
currLineIndent*: int
|
||||||
strongSpaces*: bool
|
strongSpaces*: bool
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -176,6 +178,7 @@ proc prettyTok*(tok: TToken): string =
|
||||||
else: result = tokToStr(tok)
|
else: result = tokToStr(tok)
|
||||||
|
|
||||||
proc printTok*(tok: TToken) =
|
proc printTok*(tok: TToken) =
|
||||||
|
write(stdout, tok.line, ":", tok.col, "\t")
|
||||||
write(stdout, TokTypeToStr[tok.tokType])
|
write(stdout, TokTypeToStr[tok.tokType])
|
||||||
write(stdout, " ")
|
write(stdout, " ")
|
||||||
writeln(stdout, tokToStr(tok))
|
writeln(stdout, tokToStr(tok))
|
||||||
|
|
@ -206,6 +209,7 @@ proc openLexer(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) =
|
||||||
openBaseLexer(lex, inputstream)
|
openBaseLexer(lex, inputstream)
|
||||||
lex.fileIdx = fileidx
|
lex.fileIdx = fileidx
|
||||||
lex.indentAhead = - 1
|
lex.indentAhead = - 1
|
||||||
|
lex.currLineIndent = 0
|
||||||
inc(lex.lineNumber, inputstream.lineOffset)
|
inc(lex.lineNumber, inputstream.lineOffset)
|
||||||
|
|
||||||
proc closeLexer(lex: var TLexer) =
|
proc closeLexer(lex: var TLexer) =
|
||||||
|
|
@ -709,6 +713,7 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||||
tok.strongSpaceA = 0
|
tok.strongSpaceA = 0
|
||||||
if buf[pos] > ' ':
|
if buf[pos] > ' ':
|
||||||
tok.indent = indent
|
tok.indent = indent
|
||||||
|
L.currLineIndent = indent
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
break # EndOfFile also leaves the loop
|
break # EndOfFile also leaves the loop
|
||||||
|
|
@ -718,6 +723,7 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
|
||||||
fillToken(tok)
|
fillToken(tok)
|
||||||
if L.indentAhead >= 0:
|
if L.indentAhead >= 0:
|
||||||
tok.indent = L.indentAhead
|
tok.indent = L.indentAhead
|
||||||
|
L.currLineIndent = L.indentAhead
|
||||||
L.indentAhead = -1
|
L.indentAhead = -1
|
||||||
else:
|
else:
|
||||||
tok.indent = -1
|
tok.indent = -1
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,10 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
|
||||||
elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
|
elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
|
||||||
# BUGFIX: check options in s!
|
# BUGFIX: check options in s!
|
||||||
if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
|
if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
|
||||||
message(s.info, hintXDeclaredButNotUsed, getSymRepr(s))
|
# XXX: implicit type params are currently skTypes
|
||||||
|
# maybe they can be made skGenericParam as well.
|
||||||
|
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags:
|
||||||
|
message(s.info, hintXDeclaredButNotUsed, getSymRepr(s))
|
||||||
s = nextIter(it, scope.symbols)
|
s = nextIter(it, scope.symbols)
|
||||||
|
|
||||||
proc wrongRedefinition*(info: TLineInfo, s: string) =
|
proc wrongRedefinition*(info: TLineInfo, s: string) =
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,9 @@ type
|
||||||
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
|
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
|
||||||
errXCannotBeClosure, errXMustBeCompileTime,
|
errXCannotBeClosure, errXMustBeCompileTime,
|
||||||
errCannotInferTypeOfTheLiteral,
|
errCannotInferTypeOfTheLiteral,
|
||||||
|
errCannotInferReturnType,
|
||||||
|
errGenericLambdaNotAllowed,
|
||||||
|
errCompilerDoesntSupportTarget,
|
||||||
errUser,
|
errUser,
|
||||||
warnCannotOpenFile,
|
warnCannotOpenFile,
|
||||||
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
||||||
|
|
@ -355,6 +358,11 @@ const
|
||||||
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
|
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
|
||||||
errXMustBeCompileTime: "'$1' can only be used in compile-time context",
|
errXMustBeCompileTime: "'$1' can only be used in compile-time context",
|
||||||
errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
|
errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
|
||||||
|
errCannotInferReturnType: "cannot infer the return type of the proc",
|
||||||
|
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
|
||||||
|
"it is used as an operand to another routine and the types " &
|
||||||
|
"of the generic paramers can be infered from the expected signature.",
|
||||||
|
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
|
||||||
errUser: "$1",
|
errUser: "$1",
|
||||||
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
||||||
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ proc optPar*(p: var TParser)
|
||||||
proc optInd*(p: var TParser, n: PNode)
|
proc optInd*(p: var TParser, n: PNode)
|
||||||
proc indAndComment*(p: var TParser, n: PNode)
|
proc indAndComment*(p: var TParser, n: PNode)
|
||||||
proc setBaseFlags*(n: PNode, base: TNumericalBase)
|
proc setBaseFlags*(n: PNode, base: TNumericalBase)
|
||||||
proc parseSymbol*(p: var TParser): PNode
|
proc parseSymbol*(p: var TParser, allowNil = false): PNode
|
||||||
proc parseTry(p: var TParser): PNode
|
proc parseTry(p: var TParser): PNode
|
||||||
proc parseCase(p: var TParser): PNode
|
proc parseCase(p: var TParser): PNode
|
||||||
# implementation
|
# implementation
|
||||||
|
|
@ -273,7 +273,7 @@ proc colcom(p: var TParser, n: PNode) =
|
||||||
eat(p, tkColon)
|
eat(p, tkColon)
|
||||||
skipComment(p, n)
|
skipComment(p, n)
|
||||||
|
|
||||||
proc parseSymbol(p: var TParser): PNode =
|
proc parseSymbol(p: var TParser, allowNil = false): PNode =
|
||||||
#| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`'
|
#| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`'
|
||||||
#| | IDENT
|
#| | IDENT
|
||||||
case p.tok.tokType
|
case p.tok.tokType
|
||||||
|
|
@ -312,9 +312,13 @@ proc parseSymbol(p: var TParser): PNode =
|
||||||
break
|
break
|
||||||
eat(p, tkAccent)
|
eat(p, tkAccent)
|
||||||
else:
|
else:
|
||||||
parMessage(p, errIdentifierExpected, p.tok)
|
if allowNil and p.tok.tokType == tkNil:
|
||||||
getTok(p) # BUGFIX: We must consume a token here to prevent endless loops!
|
result = newNodeP(nkNilLit, p)
|
||||||
result = ast.emptyNode
|
getTok(p)
|
||||||
|
else:
|
||||||
|
parMessage(p, errIdentifierExpected, p.tok)
|
||||||
|
getTok(p) # BUGFIX: We must consume a token here to prevent endless loops!
|
||||||
|
result = ast.emptyNode
|
||||||
|
|
||||||
proc indexExpr(p: var TParser): PNode =
|
proc indexExpr(p: var TParser): PNode =
|
||||||
#| indexExpr = expr
|
#| indexExpr = expr
|
||||||
|
|
@ -658,7 +662,7 @@ proc namedParams(p: var TParser, callee: PNode,
|
||||||
exprColonEqExprListAux(p, endTok, result)
|
exprColonEqExprListAux(p, endTok, result)
|
||||||
|
|
||||||
proc parseMacroColon(p: var TParser, x: PNode): PNode
|
proc parseMacroColon(p: var TParser, x: PNode): PNode
|
||||||
proc primarySuffix(p: var TParser, r: PNode): PNode =
|
proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||||
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
|
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
|
||||||
#| | doBlocks
|
#| | doBlocks
|
||||||
#| | '.' optInd ('type' | 'addr' | symbol) generalizedLit?
|
#| | '.' optInd ('type' | 'addr' | symbol) generalizedLit?
|
||||||
|
|
@ -666,7 +670,8 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
|
||||||
#| | '{' optInd indexExprList optPar '}'
|
#| | '{' optInd indexExprList optPar '}'
|
||||||
#| | &( '`'|IDENT|literal|'cast') expr # command syntax
|
#| | &( '`'|IDENT|literal|'cast') expr # command syntax
|
||||||
result = r
|
result = r
|
||||||
while p.tok.indent < 0:
|
while p.tok.indent < 0 or
|
||||||
|
(p.tok.tokType == tkDot and p.tok.indent >= baseIndent):
|
||||||
case p.tok.tokType
|
case p.tok.tokType
|
||||||
of tkParLe:
|
of tkParLe:
|
||||||
if p.strongSpaces and p.tok.strongSpaceA > 0: break
|
if p.strongSpaces and p.tok.strongSpaceA > 0: break
|
||||||
|
|
@ -713,9 +718,11 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
|
||||||
break
|
break
|
||||||
|
|
||||||
proc primary(p: var TParser, mode: TPrimaryMode): PNode
|
proc primary(p: var TParser, mode: TPrimaryMode): PNode
|
||||||
|
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
|
||||||
|
|
||||||
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
|
proc parseOperators(p: var TParser, headNode: PNode,
|
||||||
result = primary(p, mode)
|
limit: int, mode: TPrimaryMode): PNode =
|
||||||
|
result = headNode
|
||||||
# expand while operators have priorities higher than 'limit'
|
# expand while operators have priorities higher than 'limit'
|
||||||
var opPrec = getPrecedence(p.tok, p.strongSpaces)
|
var opPrec = getPrecedence(p.tok, p.strongSpaces)
|
||||||
let modeB = if mode == pmTypeDef: pmTypeDesc else: mode
|
let modeB = if mode == pmTypeDef: pmTypeDesc else: mode
|
||||||
|
|
@ -735,6 +742,10 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
|
||||||
result = a
|
result = a
|
||||||
opPrec = getPrecedence(p.tok, p.strongSpaces)
|
opPrec = getPrecedence(p.tok, p.strongSpaces)
|
||||||
|
|
||||||
|
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
|
||||||
|
result = primary(p, mode)
|
||||||
|
result = parseOperators(p, result, limit, mode)
|
||||||
|
|
||||||
proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
|
proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
|
||||||
result = simpleExprAux(p, -1, mode)
|
result = simpleExprAux(p, -1, mode)
|
||||||
|
|
||||||
|
|
@ -958,6 +969,15 @@ proc isExprStart(p: TParser): bool =
|
||||||
result = true
|
result = true
|
||||||
else: result = false
|
else: result = false
|
||||||
|
|
||||||
|
proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) =
|
||||||
|
while true:
|
||||||
|
var s = parseSymbol(p, allowNil)
|
||||||
|
if s.kind == nkEmpty: break
|
||||||
|
addSon(result, s)
|
||||||
|
if p.tok.tokType != tkComma: break
|
||||||
|
getTok(p)
|
||||||
|
optInd(p, s)
|
||||||
|
|
||||||
proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
|
proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
|
||||||
mode: TPrimaryMode): PNode =
|
mode: TPrimaryMode): PNode =
|
||||||
result = newNodeP(kind, p)
|
result = newNodeP(kind, p)
|
||||||
|
|
@ -965,6 +985,13 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
|
||||||
optInd(p, result)
|
optInd(p, result)
|
||||||
if not isOperator(p.tok) and isExprStart(p):
|
if not isOperator(p.tok) and isExprStart(p):
|
||||||
addSon(result, primary(p, mode))
|
addSon(result, primary(p, mode))
|
||||||
|
if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}:
|
||||||
|
let nodeKind = if p.tok.tokType == tkWith: nkWith
|
||||||
|
else: nkWithout
|
||||||
|
getTok(p)
|
||||||
|
let list = newNodeP(nodeKind, p)
|
||||||
|
result.addSon list
|
||||||
|
parseSymbolList(p, list, allowNil = true)
|
||||||
|
|
||||||
proc parseExpr(p: var TParser): PNode =
|
proc parseExpr(p: var TParser): PNode =
|
||||||
#| expr = (ifExpr
|
#| expr = (ifExpr
|
||||||
|
|
@ -981,7 +1008,6 @@ proc parseExpr(p: var TParser): PNode =
|
||||||
|
|
||||||
proc parseEnum(p: var TParser): PNode
|
proc parseEnum(p: var TParser): PNode
|
||||||
proc parseObject(p: var TParser): PNode
|
proc parseObject(p: var TParser): PNode
|
||||||
proc parseDistinct(p: var TParser): PNode
|
|
||||||
proc parseTypeClass(p: var TParser): PNode
|
proc parseTypeClass(p: var TParser): PNode
|
||||||
|
|
||||||
proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
|
|
@ -1001,8 +1027,9 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
optInd(p, a)
|
optInd(p, a)
|
||||||
if isSigil:
|
if isSigil:
|
||||||
#XXX prefix operators
|
#XXX prefix operators
|
||||||
|
let baseInd = p.lex.currLineIndent
|
||||||
addSon(result, primary(p, pmSkipSuffix))
|
addSon(result, primary(p, pmSkipSuffix))
|
||||||
result = primarySuffix(p, result)
|
result = primarySuffix(p, result, baseInd)
|
||||||
else:
|
else:
|
||||||
addSon(result, primary(p, pmNormal))
|
addSon(result, primary(p, pmNormal))
|
||||||
return
|
return
|
||||||
|
|
@ -1065,9 +1092,10 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
optInd(p, result)
|
optInd(p, result)
|
||||||
addSon(result, primary(p, pmNormal))
|
addSon(result, primary(p, pmNormal))
|
||||||
else:
|
else:
|
||||||
|
let baseInd = p.lex.currLineIndent
|
||||||
result = identOrLiteral(p, mode)
|
result = identOrLiteral(p, mode)
|
||||||
if mode != pmSkipSuffix:
|
if mode != pmSkipSuffix:
|
||||||
result = primarySuffix(p, result)
|
result = primarySuffix(p, result, baseInd)
|
||||||
|
|
||||||
proc parseTypeDesc(p: var TParser): PNode =
|
proc parseTypeDesc(p: var TParser): PNode =
|
||||||
#| typeDesc = simpleExpr
|
#| typeDesc = simpleExpr
|
||||||
|
|
@ -1501,7 +1529,7 @@ proc parseSection(p: var TParser, kind: TNodeKind,
|
||||||
defparser: TDefParser): PNode =
|
defparser: TDefParser): PNode =
|
||||||
#| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED)
|
#| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED)
|
||||||
result = newNodeP(kind, p)
|
result = newNodeP(kind, p)
|
||||||
getTok(p)
|
if kind != nkTypeSection: getTok(p)
|
||||||
skipComment(p, result)
|
skipComment(p, result)
|
||||||
if realInd(p):
|
if realInd(p):
|
||||||
withInd(p):
|
withInd(p):
|
||||||
|
|
@ -1734,13 +1762,6 @@ proc parseTypeClass(p: var TParser): PNode =
|
||||||
else:
|
else:
|
||||||
addSon(result, parseStmt(p))
|
addSon(result, parseStmt(p))
|
||||||
|
|
||||||
proc parseDistinct(p: var TParser): PNode =
|
|
||||||
#| distinct = 'distinct' optInd typeDesc
|
|
||||||
result = newNodeP(nkDistinctTy, p)
|
|
||||||
getTok(p)
|
|
||||||
optInd(p, result)
|
|
||||||
addSon(result, parseTypeDesc(p))
|
|
||||||
|
|
||||||
proc parseTypeDef(p: var TParser): PNode =
|
proc parseTypeDef(p: var TParser): PNode =
|
||||||
#| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
|
#| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
|
||||||
#| indAndComment?
|
#| indAndComment?
|
||||||
|
|
@ -1862,7 +1883,16 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
|
||||||
of tkMacro: result = parseRoutine(p, nkMacroDef)
|
of tkMacro: result = parseRoutine(p, nkMacroDef)
|
||||||
of tkTemplate: result = parseRoutine(p, nkTemplateDef)
|
of tkTemplate: result = parseRoutine(p, nkTemplateDef)
|
||||||
of tkConverter: result = parseRoutine(p, nkConverterDef)
|
of tkConverter: result = parseRoutine(p, nkConverterDef)
|
||||||
of tkType: result = parseSection(p, nkTypeSection, parseTypeDef)
|
of tkType:
|
||||||
|
getTok(p)
|
||||||
|
if p.tok.tokType == tkParLe:
|
||||||
|
getTok(p)
|
||||||
|
result = newNodeP(nkTypeOfExpr, p)
|
||||||
|
result.addSon(primary(p, pmTypeDesc))
|
||||||
|
eat(p, tkParRi)
|
||||||
|
result = parseOperators(p, result, -1, pmNormal)
|
||||||
|
else:
|
||||||
|
result = parseSection(p, nkTypeSection, parseTypeDef)
|
||||||
of tkConst: result = parseSection(p, nkConstSection, parseConstant)
|
of tkConst: result = parseSection(p, nkConstSection, parseConstant)
|
||||||
of tkLet: result = parseSection(p, nkLetSection, parseVariable)
|
of tkLet: result = parseSection(p, nkLetSection, parseVariable)
|
||||||
of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
|
of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
|
||||||
|
|
@ -1886,7 +1916,7 @@ proc parseStmt(p: var TParser): PNode =
|
||||||
if p.tok.indent < 0 or p.tok.indent == p.currInd: discard
|
if p.tok.indent < 0 or p.tok.indent == p.currInd: discard
|
||||||
else: break
|
else: break
|
||||||
else:
|
else:
|
||||||
if p.tok.indent > p.currInd:
|
if p.tok.indent > p.currInd and p.tok.tokType != tkDot:
|
||||||
parMessage(p, errInvalidIndentation)
|
parMessage(p, errInvalidIndentation)
|
||||||
break
|
break
|
||||||
if p.tok.tokType in {tkCurlyRi, tkParRi, tkCurlyDotRi, tkBracketRi}:
|
if p.tok.tokType in {tkCurlyRi, tkParRi, tkCurlyDotRi, tkBracketRi}:
|
||||||
|
|
@ -1913,7 +1943,8 @@ proc parseStmt(p: var TParser): PNode =
|
||||||
else:
|
else:
|
||||||
result = newNodeP(nkStmtList, p)
|
result = newNodeP(nkStmtList, p)
|
||||||
while true:
|
while true:
|
||||||
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
|
if p.tok.indent >= 0:
|
||||||
|
parMessage(p, errInvalidIndentation)
|
||||||
let a = simpleStmt(p)
|
let a = simpleStmt(p)
|
||||||
if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
||||||
result.add(a)
|
result.add(a)
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ const
|
||||||
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
|
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
|
||||||
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
|
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
|
||||||
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
|
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
|
||||||
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion}
|
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked}
|
||||||
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
||||||
wImportCpp, wImportObjC, wError}
|
wImportCpp, wImportObjC, wError}
|
||||||
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
||||||
|
|
@ -655,6 +655,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
incl(sym.flags, sfThread)
|
incl(sym.flags, sfThread)
|
||||||
incl(sym.flags, sfProcvar)
|
incl(sym.flags, sfProcvar)
|
||||||
if sym.typ != nil: incl(sym.typ.flags, tfThread)
|
if sym.typ != nil: incl(sym.typ.flags, tfThread)
|
||||||
|
of wPacked:
|
||||||
|
noVal(it)
|
||||||
|
if sym.typ == nil: invalidPragma(it)
|
||||||
|
else: incl(sym.typ.flags, tfPacked)
|
||||||
of wHint: message(it.info, hintUser, expectStrLit(c, it))
|
of wHint: message(it.info, hintUser, expectStrLit(c, it))
|
||||||
of wWarning: message(it.info, warnUser, expectStrLit(c, it))
|
of wWarning: message(it.info, warnUser, expectStrLit(c, it))
|
||||||
of wError:
|
of wError:
|
||||||
|
|
|
||||||
|
|
@ -424,8 +424,11 @@ proc lsub(n: PNode): int =
|
||||||
of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref")
|
of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref")
|
||||||
of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr")
|
of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr")
|
||||||
of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var")
|
of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var")
|
||||||
of nkDistinctTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) +
|
of nkDistinctTy:
|
||||||
len("Distinct")
|
result = len("distinct") + (if n.len > 0: lsub(n.sons[0])+1 else: 0)
|
||||||
|
if n.len > 1:
|
||||||
|
result += (if n[1].kind == nkWith: len("_with_") else: len("_without_"))
|
||||||
|
result += lcomma(n[1])
|
||||||
of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) +
|
of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) +
|
||||||
len("static[]")
|
len("static[]")
|
||||||
of nkTypeDef: result = lsons(n) + 3
|
of nkTypeDef: result = lsons(n) + 3
|
||||||
|
|
@ -1020,9 +1023,15 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
else:
|
else:
|
||||||
put(g, tkVar, "var")
|
put(g, tkVar, "var")
|
||||||
of nkDistinctTy:
|
of nkDistinctTy:
|
||||||
if sonsLen(n) > 0:
|
if n.len > 0:
|
||||||
putWithSpace(g, tkDistinct, "distinct")
|
putWithSpace(g, tkDistinct, "distinct")
|
||||||
gsub(g, n.sons[0])
|
gsub(g, n.sons[0])
|
||||||
|
if n.len > 1:
|
||||||
|
if n[1].kind == nkWith:
|
||||||
|
putWithSpace(g, tkWith, " with")
|
||||||
|
else:
|
||||||
|
putWithSpace(g, tkWithout, " without")
|
||||||
|
gcomma(g, n[1])
|
||||||
else:
|
else:
|
||||||
put(g, tkDistinct, "distinct")
|
put(g, tkDistinct, "distinct")
|
||||||
of nkTypeDef:
|
of nkTypeDef:
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,9 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
|
||||||
result = copyTree(arg)
|
result = copyTree(arg)
|
||||||
result.typ = formal
|
result.typ = formal
|
||||||
|
|
||||||
|
proc inferWithMetatype(c: PContext, formal: PType,
|
||||||
|
arg: PNode, coerceDistincts = false): PNode
|
||||||
|
|
||||||
var commonTypeBegin = PType(kind: tyExpr)
|
var commonTypeBegin = PType(kind: tyExpr)
|
||||||
|
|
||||||
proc commonType*(x, y: PType): PType =
|
proc commonType*(x, y: PType): PType =
|
||||||
|
|
|
||||||
|
|
@ -228,12 +228,25 @@ proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
|
||||||
if m.genericConverter and result != nil:
|
if m.genericConverter and result != nil:
|
||||||
instGenericConvertersArg(c, result, m)
|
instGenericConvertersArg(c, result, m)
|
||||||
|
|
||||||
proc convertTo*(c: PContext, f: PType, n: PNode): PNode =
|
proc inferWithMetatype(c: PContext, formal: PType,
|
||||||
|
arg: PNode, coerceDistincts = false): PNode =
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
initCandidate(c, m, f)
|
initCandidate(c, m, formal)
|
||||||
result = paramTypesMatch(m, f, n.typ, n, nil)
|
m.coerceDistincts = coerceDistincts
|
||||||
|
result = paramTypesMatch(m, formal, arg.typ, arg, nil)
|
||||||
if m.genericConverter and result != nil:
|
if m.genericConverter and result != nil:
|
||||||
instGenericConvertersArg(c, result, m)
|
instGenericConvertersArg(c, result, m)
|
||||||
|
if result != nil:
|
||||||
|
# This almost exactly replicates the steps taken by the compiler during
|
||||||
|
# param matching. It performs an embarassing ammount of back-and-forth
|
||||||
|
# type jugling, but it's the price to pay for consistency and correctness
|
||||||
|
result.typ = generateTypeInstance(c, m.bindings, arg.info,
|
||||||
|
formal.skipTypes({tyCompositeTypeClass}))
|
||||||
|
else:
|
||||||
|
typeMismatch(arg, formal, arg.typ)
|
||||||
|
# error correction:
|
||||||
|
result = copyTree(arg)
|
||||||
|
result.typ = formal
|
||||||
|
|
||||||
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
||||||
assert x.state == csMatch
|
assert x.state == csMatch
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ type
|
||||||
|
|
||||||
TExprFlag* = enum
|
TExprFlag* = enum
|
||||||
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
|
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
|
||||||
efAllowDestructor, efWantValue
|
efAllowDestructor, efWantValue, efOperand
|
||||||
TExprFlags* = set[TExprFlag]
|
TExprFlags* = set[TExprFlag]
|
||||||
|
|
||||||
PContext* = ref TContext
|
PContext* = ref TContext
|
||||||
|
|
@ -251,6 +251,27 @@ proc makeNotType*(c: PContext, t1: PType): PType =
|
||||||
propagateToOwner(result, t1)
|
propagateToOwner(result, t1)
|
||||||
result.flags.incl(t1.flags * {tfHasStatic})
|
result.flags.incl(t1.flags * {tfHasStatic})
|
||||||
|
|
||||||
|
proc nMinusOne*(n: PNode): PNode =
|
||||||
|
result = newNode(nkCall, n.info, @[
|
||||||
|
newSymNode(getSysMagic("<", mUnaryLt)),
|
||||||
|
n])
|
||||||
|
|
||||||
|
# Remember to fix the procs below this one when you make changes!
|
||||||
|
proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
|
||||||
|
let intType = getSysType tyInt
|
||||||
|
result = newTypeS(tyRange, c)
|
||||||
|
result.sons = @[intType]
|
||||||
|
result.n = newNode(nkRange, n.info, @[
|
||||||
|
newIntTypeNode(nkIntLit, 0, intType),
|
||||||
|
makeStaticExpr(c, n.nMinusOne)])
|
||||||
|
|
||||||
|
template rangeHasStaticIf*(t: PType): bool =
|
||||||
|
# this accepts the ranges's node
|
||||||
|
t.n[1].kind == nkStaticExpr
|
||||||
|
|
||||||
|
template getStaticTypeFromRange*(t: PType): PType =
|
||||||
|
t.n[1][0][1].typ
|
||||||
|
|
||||||
proc newTypeS(kind: TTypeKind, c: PContext): PType =
|
proc newTypeS(kind: TTypeKind, c: PContext): PType =
|
||||||
result = newType(kind, getCurrOwner())
|
result = newType(kind, getCurrOwner())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
|
||||||
|
|
||||||
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
# same as 'semExprWithType' but doesn't check for proc vars
|
# same as 'semExprWithType' but doesn't check for proc vars
|
||||||
result = semExpr(c, n, flags)
|
result = semExpr(c, n, flags + {efOperand})
|
||||||
if result.kind == nkEmpty:
|
if result.kind == nkEmpty:
|
||||||
# do not produce another redundant error message:
|
# do not produce another redundant error message:
|
||||||
#raiseRecoverableError("")
|
#raiseRecoverableError("")
|
||||||
|
|
@ -117,10 +117,12 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||||
elif s.ast != nil:
|
elif s.ast != nil:
|
||||||
result = semExpr(c, s.ast)
|
result = semExpr(c, s.ast)
|
||||||
else:
|
else:
|
||||||
internalError(n.info, "no default for")
|
n.typ = s.typ
|
||||||
result = emptyNode
|
return n
|
||||||
of skType:
|
of skType:
|
||||||
markUsed(n, s)
|
markUsed(n, s)
|
||||||
|
if s.typ.kind == tyStatic and s.typ.n != nil:
|
||||||
|
return s.typ.n
|
||||||
result = newSymNode(s, n.info)
|
result = newSymNode(s, n.info)
|
||||||
result.typ = makeTypeDesc(c, s.typ)
|
result.typ = makeTypeDesc(c, s.typ)
|
||||||
else:
|
else:
|
||||||
|
|
@ -191,15 +193,35 @@ proc isCastable(dst, src: PType): bool =
|
||||||
proc isSymChoice(n: PNode): bool {.inline.} =
|
proc isSymChoice(n: PNode): bool {.inline.} =
|
||||||
result = n.kind in nkSymChoices
|
result = n.kind in nkSymChoices
|
||||||
|
|
||||||
|
proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
|
||||||
|
# XXX: liftParamType started to perform addDecl
|
||||||
|
# we could do that instead in semTypeNode by snooping for added
|
||||||
|
# gnrc. params, then it won't be necessary to open a new scope here
|
||||||
|
openScope(c)
|
||||||
|
var lifted = liftParamType(c, skType, newNodeI(nkArgList, info),
|
||||||
|
t, ":anon", info)
|
||||||
|
closeScope(c)
|
||||||
|
if lifted != nil: t = lifted
|
||||||
|
|
||||||
proc semConv(c: PContext, n: PNode): PNode =
|
proc semConv(c: PContext, n: PNode): PNode =
|
||||||
if sonsLen(n) != 2:
|
if sonsLen(n) != 2:
|
||||||
localError(n.info, errConvNeedsOneArg)
|
localError(n.info, errConvNeedsOneArg)
|
||||||
return n
|
return n
|
||||||
|
|
||||||
result = newNodeI(nkConv, n.info)
|
result = newNodeI(nkConv, n.info)
|
||||||
result.typ = semTypeNode(c, n.sons[0], nil).skipTypes({tyGenericInst})
|
var targetType = semTypeNode(c, n.sons[0], nil)
|
||||||
addSon(result, copyTree(n.sons[0]))
|
maybeLiftType(targetType, c, n[0].info)
|
||||||
addSon(result, semExprWithType(c, n.sons[1]))
|
result.addSon copyTree(n.sons[0])
|
||||||
var op = result.sons[1]
|
var op = semExprWithType(c, n.sons[1])
|
||||||
|
|
||||||
|
if targetType.isMetaType:
|
||||||
|
let final = inferWithMetatype(c, targetType, op, true)
|
||||||
|
result.addSon final
|
||||||
|
result.typ = final.typ
|
||||||
|
return
|
||||||
|
|
||||||
|
result.typ = targetType
|
||||||
|
addSon(result, op)
|
||||||
|
|
||||||
if not isSymChoice(op):
|
if not isSymChoice(op):
|
||||||
let status = checkConvertible(c, result.typ, op.typ)
|
let status = checkConvertible(c, result.typ, op.typ)
|
||||||
|
|
@ -221,7 +243,7 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||||
for i in countup(0, sonsLen(op) - 1):
|
for i in countup(0, sonsLen(op) - 1):
|
||||||
let it = op.sons[i]
|
let it = op.sons[i]
|
||||||
let status = checkConvertible(c, result.typ, it.typ)
|
let status = checkConvertible(c, result.typ, it.typ)
|
||||||
if status == convOK:
|
if status in {convOK, convNotNeedeed}:
|
||||||
markUsed(n, it.sym)
|
markUsed(n, it.sym)
|
||||||
markIndirect(c, it.sym)
|
markIndirect(c, it.sym)
|
||||||
return it
|
return it
|
||||||
|
|
@ -325,9 +347,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
|
||||||
tfIterator notin t.flags))
|
tfIterator notin t.flags))
|
||||||
else:
|
else:
|
||||||
var t2 = n[2].typ.skipTypes({tyTypeDesc})
|
var t2 = n[2].typ.skipTypes({tyTypeDesc})
|
||||||
let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info),
|
maybeLiftType(t2, c, n.info)
|
||||||
t2, ":anon", n.info)
|
|
||||||
if lifted != nil: t2 = lifted
|
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
initCandidate(c, m, t2)
|
initCandidate(c, m, t2)
|
||||||
let match = typeRel(m, t2, t1) != isNone
|
let match = typeRel(m, t2, t1) != isNone
|
||||||
|
|
@ -613,6 +633,18 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||||
else: return result
|
else: return result
|
||||||
result.typ = semfold.getIntervalType(callee.magic, call)
|
result.typ = semfold.getIntervalType(callee.magic, call)
|
||||||
|
|
||||||
|
block maybeLabelAsStatic:
|
||||||
|
# XXX: temporary work-around needed for tlateboundstatic.
|
||||||
|
# This is certainly not correct, but it will get the job
|
||||||
|
# done until we have a more robust infrastructure for
|
||||||
|
# implicit statics.
|
||||||
|
if n.len > 1:
|
||||||
|
for i in 1 .. <n.len:
|
||||||
|
if n[i].typ.kind != tyStatic or tfUnresolved notin n[i].typ.flags:
|
||||||
|
break maybeLabelAsStatic
|
||||||
|
n.typ = newTypeWithSons(c, tyStatic, @[n.typ])
|
||||||
|
n.typ.flags.incl tfUnresolved
|
||||||
|
|
||||||
# optimization pass: not necessary for correctness of the semantic pass
|
# optimization pass: not necessary for correctness of the semantic pass
|
||||||
if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and
|
if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and
|
||||||
{sfForward, sfImportc} * callee.flags == {}:
|
{sfForward, sfImportc} * callee.flags == {}:
|
||||||
|
|
@ -899,10 +931,15 @@ proc makeDeref(n: PNode): PNode =
|
||||||
addSon(result, a)
|
addSon(result, a)
|
||||||
t = skipTypes(t.sons[0], {tyGenericInst})
|
t = skipTypes(t.sons[0], {tyGenericInst})
|
||||||
|
|
||||||
proc readTypeParameter(c: PContext, ty: PType,
|
const
|
||||||
|
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
|
||||||
|
tyDotOpTransparent = {tyVar, tyPtr, tyRef}
|
||||||
|
|
||||||
|
proc readTypeParameter(c: PContext, typ: PType,
|
||||||
paramName: PIdent, info: TLineInfo): PNode =
|
paramName: PIdent, info: TLineInfo): PNode =
|
||||||
internalAssert ty.kind == tyGenericInst
|
let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias
|
||||||
let ty = ty.skipGenericAlias
|
else: (internalAssert(typ.kind == tyCompositeTypeClass); typ.sons[1])
|
||||||
|
|
||||||
let tbody = ty.sons[0]
|
let tbody = ty.sons[0]
|
||||||
for s in countup(0, tbody.len-2):
|
for s in countup(0, tbody.len-2):
|
||||||
let tParam = tbody.sons[s]
|
let tParam = tbody.sons[s]
|
||||||
|
|
@ -933,6 +970,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
result = nil
|
result = nil
|
||||||
if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone):
|
if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone):
|
||||||
if ty.kind == tyTypeDesc: ty = ty.base
|
if ty.kind == tyTypeDesc: ty = ty.base
|
||||||
|
ty = ty.skipTypes(tyDotOpTransparent)
|
||||||
case ty.kind
|
case ty.kind
|
||||||
of tyEnum:
|
of tyEnum:
|
||||||
# look up if the identifier belongs to the enum:
|
# look up if the identifier belongs to the enum:
|
||||||
|
|
@ -946,7 +984,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
result.typ = ty
|
result.typ = ty
|
||||||
markUsed(n, f)
|
markUsed(n, f)
|
||||||
return
|
return
|
||||||
of tyGenericInst:
|
of tyTypeParamsHolders:
|
||||||
return readTypeParameter(c, ty, i, n.info)
|
return readTypeParameter(c, ty, i, n.info)
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
if ty.n.kind == nkRecList:
|
if ty.n.kind == nkRecList:
|
||||||
|
|
@ -996,8 +1034,10 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
# we didn't find any field, let's look for a generic param
|
# we didn't find any field, let's look for a generic param
|
||||||
if result == nil and n.sons[0].typ.kind == tyGenericInst:
|
if result == nil:
|
||||||
result = readTypeParameter(c, n.sons[0].typ, i, n.info)
|
let t = n.sons[0].typ.skipTypes(tyDotOpTransparent)
|
||||||
|
if t.kind in tyTypeParamsHolders:
|
||||||
|
result = readTypeParameter(c, t, i, n.info)
|
||||||
|
|
||||||
proc dotTransformation(c: PContext, n: PNode): PNode =
|
proc dotTransformation(c: PContext, n: PNode): PNode =
|
||||||
if isSymChoice(n.sons[1]):
|
if isSymChoice(n.sons[1]):
|
||||||
|
|
@ -1182,7 +1222,7 @@ proc semAsgn(c: PContext, n: PNode): PNode =
|
||||||
if lhsIsResult: {efAllowDestructor} else: {})
|
if lhsIsResult: {efAllowDestructor} else: {})
|
||||||
if lhsIsResult:
|
if lhsIsResult:
|
||||||
n.typ = enforceVoidContext
|
n.typ = enforceVoidContext
|
||||||
if resultTypeIsInferrable(lhs.sym.typ):
|
if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
|
||||||
if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric:
|
if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric:
|
||||||
internalAssert c.p.resultSym != nil
|
internalAssert c.p.resultSym != nil
|
||||||
lhs.typ = rhs.typ
|
lhs.typ = rhs.typ
|
||||||
|
|
@ -1218,6 +1258,7 @@ proc semReturn(c: PContext, n: PNode): PNode =
|
||||||
|
|
||||||
proc semProcBody(c: PContext, n: PNode): PNode =
|
proc semProcBody(c: PContext, n: PNode): PNode =
|
||||||
openScope(c)
|
openScope(c)
|
||||||
|
|
||||||
result = semExpr(c, n)
|
result = semExpr(c, n)
|
||||||
if c.p.resultSym != nil and not isEmptyType(result.typ):
|
if c.p.resultSym != nil and not isEmptyType(result.typ):
|
||||||
# transform ``expr`` to ``result = expr``, but not if the expr is already
|
# transform ``expr`` to ``result = expr``, but not if the expr is already
|
||||||
|
|
@ -1241,6 +1282,11 @@ proc semProcBody(c: PContext, n: PNode): PNode =
|
||||||
result = semAsgn(c, a)
|
result = semAsgn(c, a)
|
||||||
else:
|
else:
|
||||||
discardCheck(c, result)
|
discardCheck(c, result)
|
||||||
|
|
||||||
|
if c.p.owner.kind notin {skMacro, skTemplate} and
|
||||||
|
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
|
||||||
|
localError(c.p.resultSym.info, errCannotInferReturnType)
|
||||||
|
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
|
|
||||||
proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
|
proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
|
||||||
if n.kind != nkGenericParams:
|
if n.kind != nkGenericParams:
|
||||||
internalError(n.info, "instantiateGenericParamList; no generic params")
|
internalError(n.info, "instantiateGenericParamList; no generic params")
|
||||||
newSeq(entry.concreteTypes, n.len)
|
newSeq(entry.concreteTypes, n.len)
|
||||||
for i in countup(0, n.len - 1):
|
for i, a in n.pairs:
|
||||||
var a = n.sons[i]
|
|
||||||
if a.kind != nkSym:
|
if a.kind != nkSym:
|
||||||
internalError(a.info, "instantiateGenericParamList; no symbol")
|
internalError(a.info, "instantiateGenericParamList; no symbol")
|
||||||
var q = a.sym
|
var q = a.sym
|
||||||
|
|
@ -86,19 +85,21 @@ proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
|
||||||
|
|
||||||
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
|
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
|
||||||
|
|
||||||
|
proc addProcDecls(c: PContext, fn: PSym) =
|
||||||
|
# get the proc itself in scope (e.g. for recursion)
|
||||||
|
addDecl(c, fn)
|
||||||
|
|
||||||
|
for i in 1 .. <fn.typ.n.len:
|
||||||
|
var param = fn.typ.n.sons[i].sym
|
||||||
|
param.owner = fn
|
||||||
|
addParamOrResult(c, param, fn.kind)
|
||||||
|
|
||||||
|
maybeAddResult(c, fn, fn.ast)
|
||||||
|
|
||||||
proc instantiateBody(c: PContext, n: PNode, result: PSym) =
|
proc instantiateBody(c: PContext, n: PNode, result: PSym) =
|
||||||
if n.sons[bodyPos].kind != nkEmpty:
|
if n.sons[bodyPos].kind != nkEmpty:
|
||||||
inc c.inGenericInst
|
inc c.inGenericInst
|
||||||
# add it here, so that recursive generic procs are possible:
|
# add it here, so that recursive generic procs are possible:
|
||||||
addDecl(c, result)
|
|
||||||
pushProcCon(c, result)
|
|
||||||
# add params to scope
|
|
||||||
for i in 1 .. <result.typ.n.len:
|
|
||||||
var param = result.typ.n.sons[i].sym
|
|
||||||
param.owner = result
|
|
||||||
addParamOrResult(c, param, result.kind)
|
|
||||||
# debug result.typ.n
|
|
||||||
maybeAddResult(c, result, n)
|
|
||||||
var b = n.sons[bodyPos]
|
var b = n.sons[bodyPos]
|
||||||
var symMap: TIdTable
|
var symMap: TIdTable
|
||||||
initIdTable symMap
|
initIdTable symMap
|
||||||
|
|
@ -108,7 +109,6 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
|
||||||
n.sons[bodyPos] = transformBody(c.module, b, result)
|
n.sons[bodyPos] = transformBody(c.module, b, result)
|
||||||
#echo "code instantiated ", result.name.s
|
#echo "code instantiated ", result.name.s
|
||||||
excl(result.flags, sfForward)
|
excl(result.flags, sfForward)
|
||||||
popProcCon(c)
|
|
||||||
dec c.inGenericInst
|
dec c.inGenericInst
|
||||||
|
|
||||||
proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
|
proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
|
||||||
|
|
@ -145,11 +145,56 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
|
||||||
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
|
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
|
||||||
result = instGenericContainer(c, n.info, header)
|
result = instGenericContainer(c, n.info, header)
|
||||||
|
|
||||||
|
proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||||
|
prc: PSym, info: TLineInfo) =
|
||||||
|
# XXX: Instantiates a generic proc signature, while at the same
|
||||||
|
# time adding the instantiated proc params into the current scope.
|
||||||
|
# This is necessary, because the instantiation process may refer to
|
||||||
|
# these params in situations like this:
|
||||||
|
# proc foo[Container](a: Container, b: a.type.Item): type(b.x)
|
||||||
|
#
|
||||||
|
# Alas, doing this here is probably not enough, because another
|
||||||
|
# proc signature could appear in the params:
|
||||||
|
# proc foo[T](a: proc (x: T, b: type(x.y))
|
||||||
|
#
|
||||||
|
# The solution would be to move this logic into semtypinst, but
|
||||||
|
# at this point semtypinst have to become part of sem, because it
|
||||||
|
# will need to use openScope, addDecl, etc
|
||||||
|
#
|
||||||
|
addDecl(c, prc)
|
||||||
|
|
||||||
|
pushInfoContext(info)
|
||||||
|
var cl = initTypeVars(c, pt, info)
|
||||||
|
var result = instCopyType(cl, prc.typ)
|
||||||
|
let originalParams = result.n
|
||||||
|
result.n = originalParams.shallowCopy
|
||||||
|
|
||||||
|
for i in 1 .. <result.len:
|
||||||
|
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
|
||||||
|
propagateToOwner(result, result.sons[i])
|
||||||
|
let param = replaceTypeVarsN(cl, originalParams[i])
|
||||||
|
result.n.sons[i] = param
|
||||||
|
if param.kind == nkSym:
|
||||||
|
# XXX: this won't be true for void params
|
||||||
|
# implement pass-through of void params and
|
||||||
|
# the "sort by distance to point" container
|
||||||
|
param.sym.owner = prc
|
||||||
|
addDecl(c, param.sym)
|
||||||
|
|
||||||
|
result.sons[0] = replaceTypeVarsT(cl, result.sons[0])
|
||||||
|
result.n.sons[0] = originalParams[0].copyTree
|
||||||
|
|
||||||
|
eraseVoidParams(result)
|
||||||
|
skipIntLiteralParams(result)
|
||||||
|
|
||||||
|
prc.typ = result
|
||||||
|
maybeAddResult(c, prc, prc.ast)
|
||||||
|
popInfoContext()
|
||||||
|
|
||||||
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
info: TLineInfo): PSym =
|
info: TLineInfo): PSym =
|
||||||
# no need to instantiate generic templates/macros:
|
# no need to instantiate generic templates/macros:
|
||||||
if fn.kind in {skTemplate, skMacro}: return fn
|
if fn.kind in {skTemplate, skMacro}: return fn
|
||||||
|
|
||||||
# generates an instantiated proc
|
# generates an instantiated proc
|
||||||
if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
|
if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
|
||||||
inc(c.instCounter)
|
inc(c.instCounter)
|
||||||
|
|
@ -173,7 +218,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
var entry = TInstantiation.new
|
var entry = TInstantiation.new
|
||||||
entry.sym = result
|
entry.sym = result
|
||||||
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
|
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
|
||||||
result.typ = generateTypeInstance(c, pt, info, fn.typ)
|
pushProcCon(c, result)
|
||||||
|
instantiateProcType(c, pt, result, info)
|
||||||
n.sons[genericParamsPos] = ast.emptyNode
|
n.sons[genericParamsPos] = ast.emptyNode
|
||||||
var oldPrc = genericCacheGet(fn, entry[])
|
var oldPrc = genericCacheGet(fn, entry[])
|
||||||
if oldPrc == nil:
|
if oldPrc == nil:
|
||||||
|
|
@ -183,12 +229,12 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
|
pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
|
||||||
if isNil(n.sons[bodyPos]):
|
if isNil(n.sons[bodyPos]):
|
||||||
n.sons[bodyPos] = copyTree(fn.getBody)
|
n.sons[bodyPos] = copyTree(fn.getBody)
|
||||||
if fn.kind != skTemplate:
|
instantiateBody(c, n, result)
|
||||||
instantiateBody(c, n, result)
|
sideEffectsCheck(c, result)
|
||||||
sideEffectsCheck(c, result)
|
|
||||||
paramsTypeCheck(c, result.typ)
|
paramsTypeCheck(c, result.typ)
|
||||||
else:
|
else:
|
||||||
result = oldPrc
|
result = oldPrc
|
||||||
|
popProcCon(c)
|
||||||
popInfoContext()
|
popInfoContext()
|
||||||
closeScope(c) # close scope for parameters
|
closeScope(c) # close scope for parameters
|
||||||
popOwner()
|
popOwner()
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,6 @@
|
||||||
|
|
||||||
var enforceVoidContext = PType(kind: tyStmt)
|
var enforceVoidContext = PType(kind: tyStmt)
|
||||||
|
|
||||||
proc semCommand(c: PContext, n: PNode): PNode =
|
|
||||||
result = semExprNoType(c, n)
|
|
||||||
|
|
||||||
proc semDiscard(c: PContext, n: PNode): PNode =
|
proc semDiscard(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
|
|
@ -133,6 +130,7 @@ proc fixNilType(n: PNode) =
|
||||||
n.typ = nil
|
n.typ = nil
|
||||||
|
|
||||||
proc discardCheck(c: PContext, result: PNode) =
|
proc discardCheck(c: PContext, result: PNode) =
|
||||||
|
if c.inTypeClass > 0: return
|
||||||
if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
|
if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
|
||||||
if result.kind == nkNilLit:
|
if result.kind == nkNilLit:
|
||||||
result.typ = nil
|
result.typ = nil
|
||||||
|
|
@ -143,11 +141,6 @@ proc discardCheck(c: PContext, result: PNode) =
|
||||||
while n.kind in skipForDiscardable:
|
while n.kind in skipForDiscardable:
|
||||||
n = n.lastSon
|
n = n.lastSon
|
||||||
n.typ = nil
|
n.typ = nil
|
||||||
elif c.inTypeClass > 0:
|
|
||||||
if result.typ.kind == tyBool:
|
|
||||||
let verdict = semConstExpr(c, result)
|
|
||||||
if verdict.intVal == 0:
|
|
||||||
localError(result.info, "type class predicate failed")
|
|
||||||
elif result.typ.kind != tyError and gCmd != cmdInteractive:
|
elif result.typ.kind != tyError and gCmd != cmdInteractive:
|
||||||
if result.typ.kind == tyNil:
|
if result.typ.kind == tyNil:
|
||||||
fixNilType(result)
|
fixNilType(result)
|
||||||
|
|
@ -944,13 +937,15 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
|
localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
|
||||||
#if efDetermineType notin flags:
|
#if efDetermineType notin flags:
|
||||||
# XXX not good enough; see tnamedparamanonproc.nim
|
# XXX not good enough; see tnamedparamanonproc.nim
|
||||||
if n.sons[genericParamsPos].kind == nkEmpty:
|
if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags):
|
||||||
pushProcCon(c, s)
|
pushProcCon(c, s)
|
||||||
addResult(c, s.typ.sons[0], n.info, skProc)
|
addResult(c, s.typ.sons[0], n.info, skProc)
|
||||||
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
||||||
addResultNode(c, n)
|
addResultNode(c, n)
|
||||||
popProcCon(c)
|
popProcCon(c)
|
||||||
|
elif efOperand notin flags:
|
||||||
|
localError(n.info, errGenericLambdaNotAllowed)
|
||||||
sideEffectsCheck(c, s)
|
sideEffectsCheck(c, s)
|
||||||
else:
|
else:
|
||||||
localError(n.info, errImplOfXexpected, s.name.s)
|
localError(n.info, errImplOfXexpected, s.name.s)
|
||||||
|
|
@ -1322,13 +1317,17 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
n.sons[i] = semExpr(c, n.sons[i])
|
n.sons[i] = semExpr(c, n.sons[i])
|
||||||
|
if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool:
|
||||||
|
let verdict = semConstExpr(c, n[i])
|
||||||
|
if verdict.intVal == 0:
|
||||||
|
localError(result.info, "type class predicate failed")
|
||||||
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
|
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
|
||||||
voidContext = true
|
voidContext = true
|
||||||
n.typ = enforceVoidContext
|
n.typ = enforceVoidContext
|
||||||
if i == last and (length == 1 or efWantValue in flags):
|
if i == last and (length == 1 or efWantValue in flags):
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||||
elif i != last or voidContext or c.inTypeClass > 0:
|
elif i != last or voidContext:
|
||||||
discardCheck(c, n.sons[i])
|
discardCheck(c, n.sons[i])
|
||||||
else:
|
else:
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
|
|
|
||||||
|
|
@ -141,11 +141,10 @@ proc semVarType(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = newConstraint(c, tyVar)
|
result = newConstraint(c, tyVar)
|
||||||
|
|
||||||
proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
|
proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
|
||||||
if sonsLen(n) == 1:
|
if n.len == 0: return newConstraint(c, tyDistinct)
|
||||||
result = newOrPrevType(tyDistinct, prev, c)
|
result = newOrPrevType(tyDistinct, prev, c)
|
||||||
addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil))
|
addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil))
|
||||||
else:
|
if n.len > 1: result.n = n[1]
|
||||||
result = newConstraint(c, tyDistinct)
|
|
||||||
|
|
||||||
proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||||
assert isRange(n)
|
assert isRange(n)
|
||||||
|
|
@ -198,11 +197,6 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
||||||
localError(n.info, errXExpectsOneTypeParam, "range")
|
localError(n.info, errXExpectsOneTypeParam, "range")
|
||||||
result = newOrPrevType(tyError, prev, c)
|
result = newOrPrevType(tyError, prev, c)
|
||||||
|
|
||||||
proc nMinusOne(n: PNode): PNode =
|
|
||||||
result = newNode(nkCall, n.info, @[
|
|
||||||
newSymNode(getSysMagic("<", mUnaryLt)),
|
|
||||||
n])
|
|
||||||
|
|
||||||
proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||||
var indx, base: PType
|
var indx, base: PType
|
||||||
result = newOrPrevType(tyArray, prev, c)
|
result = newOrPrevType(tyArray, prev, c)
|
||||||
|
|
@ -212,7 +206,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||||
else:
|
else:
|
||||||
let e = semExprWithType(c, n.sons[1], {efDetermineType})
|
let e = semExprWithType(c, n.sons[1], {efDetermineType})
|
||||||
if e.typ.kind == tyFromExpr:
|
if e.typ.kind == tyFromExpr:
|
||||||
indx = e.typ
|
indx = makeRangeWithStaticExpr(c, e.typ.n)
|
||||||
elif e.kind in {nkIntLit..nkUInt64Lit}:
|
elif e.kind in {nkIntLit..nkUInt64Lit}:
|
||||||
indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
|
indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
|
||||||
elif e.kind == nkSym and e.typ.kind == tyStatic:
|
elif e.kind == nkSym and e.typ.kind == tyStatic:
|
||||||
|
|
@ -220,7 +214,8 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||||
internalAssert c.inGenericContext > 0
|
internalAssert c.inGenericContext > 0
|
||||||
if not isOrdinalType(e.typ.lastSon):
|
if not isOrdinalType(e.typ.lastSon):
|
||||||
localError(n[1].info, errOrdinalTypeExpected)
|
localError(n[1].info, errOrdinalTypeExpected)
|
||||||
indx = e.typ
|
indx = makeRangeWithStaticExpr(c, e)
|
||||||
|
indx.flags.incl tfUnresolved
|
||||||
elif e.kind in nkCallKinds and hasGenericArguments(e):
|
elif e.kind in nkCallKinds and hasGenericArguments(e):
|
||||||
if not isOrdinalType(e.typ):
|
if not isOrdinalType(e.typ):
|
||||||
localError(n[1].info, errOrdinalTypeExpected)
|
localError(n[1].info, errOrdinalTypeExpected)
|
||||||
|
|
@ -229,12 +224,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||||
# We are going to construct a range type that will be
|
# We are going to construct a range type that will be
|
||||||
# properly filled-out in semtypinst (see how tyStaticExpr
|
# properly filled-out in semtypinst (see how tyStaticExpr
|
||||||
# is handled there).
|
# is handled there).
|
||||||
let intType = getSysType(tyInt)
|
indx = makeRangeWithStaticExpr(c, e)
|
||||||
indx = newTypeS(tyRange, c)
|
|
||||||
indx.sons = @[intType]
|
|
||||||
indx.n = newNode(nkRange, n.info, @[
|
|
||||||
newIntTypeNode(nkIntLit, 0, intType),
|
|
||||||
makeStaticExpr(c, e.nMinusOne)])
|
|
||||||
else:
|
else:
|
||||||
indx = e.typ.skipTypes({tyTypeDesc})
|
indx = e.typ.skipTypes({tyTypeDesc})
|
||||||
addSonSkipIntLit(result, indx)
|
addSonSkipIntLit(result, indx)
|
||||||
|
|
@ -283,6 +273,18 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||||
result = result.typ.sym.copySym
|
result = result.typ.sym.copySym
|
||||||
result.typ = copyType(result.typ, result.typ.owner, true)
|
result.typ = copyType(result.typ, result.typ.owner, true)
|
||||||
result.typ.flags.incl tfUnresolved
|
result.typ.flags.incl tfUnresolved
|
||||||
|
|
||||||
|
if result.kind == skGenericParam:
|
||||||
|
if result.typ.kind == tyGenericParam and result.typ.len == 0 and
|
||||||
|
tfWildcard in result.typ.flags:
|
||||||
|
# collapse the wild-card param to a type
|
||||||
|
result.kind = skType
|
||||||
|
result.typ.flags.excl tfWildcard
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
localError(n.info, errTypeExpected)
|
||||||
|
return errorSym(c, n)
|
||||||
|
|
||||||
if result.kind != skType:
|
if result.kind != skType:
|
||||||
# this implements the wanted ``var v: V, x: V`` feature ...
|
# this implements the wanted ``var v: V, x: V`` feature ...
|
||||||
var ov: TOverloadIter
|
var ov: TOverloadIter
|
||||||
|
|
@ -613,17 +615,28 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
if base == nil and tfInheritable notin result.flags:
|
if base == nil and tfInheritable notin result.flags:
|
||||||
incl(result.flags, tfFinal)
|
incl(result.flags, tfFinal)
|
||||||
|
|
||||||
|
proc findEnforcedStaticType(t: PType): PType =
|
||||||
|
# This handles types such as `static[T] and Foo`,
|
||||||
|
# which are subset of `static[T]`, hence they could
|
||||||
|
# be treated in the same way
|
||||||
|
if t.kind == tyStatic: return t
|
||||||
|
if t.kind == tyAnd:
|
||||||
|
for s in t.sons:
|
||||||
|
let t = findEnforcedStaticType(s)
|
||||||
|
if t != nil: return t
|
||||||
|
|
||||||
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
||||||
template addDecl(x) =
|
template addDecl(x) =
|
||||||
if sfGenSym notin x.flags: addDecl(c, x)
|
if sfGenSym notin x.flags: addDecl(c, x)
|
||||||
|
|
||||||
if kind == skMacro:
|
if kind == skMacro:
|
||||||
if param.typ.kind == tyTypeDesc:
|
let staticType = findEnforcedStaticType(param.typ)
|
||||||
addDecl(param)
|
if staticType != nil:
|
||||||
elif param.typ.kind == tyStatic:
|
|
||||||
var a = copySym(param)
|
var a = copySym(param)
|
||||||
a.typ = param.typ.base
|
a.typ = staticType.base
|
||||||
addDecl(a)
|
addDecl(a)
|
||||||
|
elif param.typ.kind == tyTypeDesc:
|
||||||
|
addDecl(param)
|
||||||
else:
|
else:
|
||||||
# within a macro, every param has the type PNimrodNode!
|
# within a macro, every param has the type PNimrodNode!
|
||||||
let nn = getSysSym"PNimrodNode"
|
let nn = getSysSym"PNimrodNode"
|
||||||
|
|
@ -665,6 +678,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
s.position = genericParams.len
|
s.position = genericParams.len
|
||||||
genericParams.addSon(newSymNode(s))
|
genericParams.addSon(newSymNode(s))
|
||||||
result = typeClass
|
result = typeClass
|
||||||
|
addDecl(c, s)
|
||||||
|
|
||||||
# XXX: There are codegen errors if this is turned into a nested proc
|
# XXX: There are codegen errors if this is turned into a nested proc
|
||||||
template liftingWalk(typ: PType, anonFlag = false): expr =
|
template liftingWalk(typ: PType, anonFlag = false): expr =
|
||||||
|
|
@ -688,11 +702,13 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
of tyStatic:
|
of tyStatic:
|
||||||
# proc(a: expr{string}, b: expr{nkLambda})
|
# proc(a: expr{string}, b: expr{nkLambda})
|
||||||
# overload on compile time values and AST trees
|
# overload on compile time values and AST trees
|
||||||
|
if paramType.n != nil: return # this is a concrete type
|
||||||
|
if tfUnresolved in paramType.flags: return # already lifted
|
||||||
let base = paramType.base.maybeLift
|
let base = paramType.base.maybeLift
|
||||||
if base.isMetaType and procKind == skMacro:
|
if base.isMetaType and procKind == skMacro:
|
||||||
localError(info, errMacroBodyDependsOnGenericTypes, paramName)
|
localError(info, errMacroBodyDependsOnGenericTypes, paramName)
|
||||||
result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base]))
|
result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base]))
|
||||||
result.flags.incl tfHasStatic
|
result.flags.incl({tfHasStatic, tfUnresolved})
|
||||||
|
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
if tfUnresolved notin paramType.flags:
|
if tfUnresolved notin paramType.flags:
|
||||||
|
|
@ -765,7 +781,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
result = paramType
|
result = paramType
|
||||||
result.lastSon.shouldHaveMeta
|
result.lastSon.shouldHaveMeta
|
||||||
|
|
||||||
let liftBody = liftingWalk(paramType.lastSon)
|
let liftBody = liftingWalk(paramType.lastSon, true)
|
||||||
if liftBody != nil:
|
if liftBody != nil:
|
||||||
result = liftBody
|
result = liftBody
|
||||||
result.shouldHaveMeta
|
result.shouldHaveMeta
|
||||||
|
|
@ -777,7 +793,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
|
|
||||||
let expanded = instGenericContainer(c, info, paramType,
|
let expanded = instGenericContainer(c, info, paramType,
|
||||||
allowMetaTypes = true)
|
allowMetaTypes = true)
|
||||||
result = liftingWalk(expanded)
|
result = liftingWalk(expanded, true)
|
||||||
|
|
||||||
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
|
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
|
||||||
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
|
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
|
||||||
|
|
@ -787,11 +803,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
result = addImplicitGeneric(newTypeS(tyAnything, c))
|
result = addImplicitGeneric(newTypeS(tyAnything, c))
|
||||||
|
|
||||||
of tyGenericParam:
|
of tyGenericParam:
|
||||||
if tfGenericTypeParam in paramType.flags and false:
|
markUsed(genericParams, paramType.sym)
|
||||||
if paramType.sonsLen > 0:
|
if tfWildcard in paramType.flags:
|
||||||
result = liftingWalk(paramType.lastSon)
|
paramType.flags.excl tfWildcard
|
||||||
else:
|
paramType.sym.kind = skType
|
||||||
result = addImplicitGeneric(newTypeS(tyAnything, c))
|
|
||||||
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
|
|
@ -894,6 +909,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
result.sons[0] = r
|
result.sons[0] = r
|
||||||
res.typ = r
|
res.typ = r
|
||||||
|
|
||||||
|
if genericParams != nil:
|
||||||
|
for n in genericParams:
|
||||||
|
if tfWildcard in n.sym.typ.flags:
|
||||||
|
n.sym.kind = skType
|
||||||
|
n.sym.typ.flags.excl tfWildcard
|
||||||
|
|
||||||
proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
|
proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
|
||||||
checkMinSonsLen(n, 1)
|
checkMinSonsLen(n, 1)
|
||||||
var length = sonsLen(n)
|
var length = sonsLen(n)
|
||||||
|
|
@ -922,8 +943,17 @@ proc semGenericParamInInvokation(c: PContext, n: PNode): PType =
|
||||||
result = semTypeNode(c, n, nil)
|
result = semTypeNode(c, n, nil)
|
||||||
|
|
||||||
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||||
|
if s.typ == nil:
|
||||||
|
localError(n.info, "cannot instantiate the '$1' $2" %
|
||||||
|
[s.name.s, ($s.kind).substr(2).toLower])
|
||||||
|
return newOrPrevType(tyError, prev, c)
|
||||||
|
|
||||||
|
var t = s.typ
|
||||||
|
if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
|
||||||
|
t = t.base
|
||||||
|
|
||||||
result = newOrPrevType(tyGenericInvokation, prev, c)
|
result = newOrPrevType(tyGenericInvokation, prev, c)
|
||||||
addSonSkipIntLit(result, s.typ)
|
addSonSkipIntLit(result, t)
|
||||||
|
|
||||||
template addToResult(typ) =
|
template addToResult(typ) =
|
||||||
if typ.isNil:
|
if typ.isNil:
|
||||||
|
|
@ -931,27 +961,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||||
rawAddSon(result, typ)
|
rawAddSon(result, typ)
|
||||||
else: addSonSkipIntLit(result, typ)
|
else: addSonSkipIntLit(result, typ)
|
||||||
|
|
||||||
if s.typ == nil:
|
if t.kind == tyForward:
|
||||||
localError(n.info, errCannotInstantiateX, s.name.s)
|
|
||||||
return newOrPrevType(tyError, prev, c)
|
|
||||||
elif s.typ.kind == tyForward:
|
|
||||||
for i in countup(1, sonsLen(n)-1):
|
for i in countup(1, sonsLen(n)-1):
|
||||||
var elem = semGenericParamInInvokation(c, n.sons[i])
|
var elem = semGenericParamInInvokation(c, n.sons[i])
|
||||||
addToResult(elem)
|
addToResult(elem)
|
||||||
elif s.typ.kind != tyGenericBody:
|
return
|
||||||
|
elif t.kind != tyGenericBody:
|
||||||
#we likely got code of the form TypeA[TypeB] where TypeA is
|
#we likely got code of the form TypeA[TypeB] where TypeA is
|
||||||
#not generic.
|
#not generic.
|
||||||
localError(n.info, errNoGenericParamsAllowedForX, s.name.s)
|
localError(n.info, errNoGenericParamsAllowedForX, s.name.s)
|
||||||
return newOrPrevType(tyError, prev, c)
|
return newOrPrevType(tyError, prev, c)
|
||||||
else:
|
else:
|
||||||
|
var m = newCandidate(c, t)
|
||||||
var m = newCandidate(c, s, n)
|
|
||||||
matches(c, n, copyTree(n), m)
|
matches(c, n, copyTree(n), m)
|
||||||
|
|
||||||
if m.state != csMatch:
|
if m.state != csMatch:
|
||||||
var err = "cannot instantiate " & typeToString(s.typ) & "\n" &
|
var err = "cannot instantiate " & typeToString(t) & "\n" &
|
||||||
"got: (" & describeArgs(c, n) & ")\n" &
|
"got: (" & describeArgs(c, n) & ")\n" &
|
||||||
"but expected: (" & describeArgs(c, s.typ.n, 0) & ")"
|
"but expected: (" & describeArgs(c, t.n, 0) & ")"
|
||||||
localError(n.info, errGenerated, err)
|
localError(n.info, errGenerated, err)
|
||||||
return newOrPrevType(tyError, prev, c)
|
return newOrPrevType(tyError, prev, c)
|
||||||
|
|
||||||
|
|
@ -963,7 +990,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||||
addToResult(typ)
|
addToResult(typ)
|
||||||
|
|
||||||
if isConcrete:
|
if isConcrete:
|
||||||
if s.ast == nil:
|
if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
|
||||||
|
# XXX: What kind of error is this? is it still relevant?
|
||||||
localError(n.info, errCannotInstantiateX, s.name.s)
|
localError(n.info, errCannotInstantiateX, s.name.s)
|
||||||
result = newOrPrevType(tyError, prev, c)
|
result = newOrPrevType(tyError, prev, c)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1090,13 +1118,17 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
for i in countup(1, n.len - 1):
|
for i in countup(1, n.len - 1):
|
||||||
result.rawAddSon(semTypeNode(c, n.sons[i], nil))
|
result.rawAddSon(semTypeNode(c, n.sons[i], nil))
|
||||||
else: result = semGeneric(c, n, s, prev)
|
else: result = semGeneric(c, n, s, prev)
|
||||||
of nkIdent, nkDotExpr, nkAccQuoted:
|
of nkDotExpr:
|
||||||
if n.kind == nkDotExpr:
|
var typeExpr = semExpr(c, n)
|
||||||
let head = qualifiedLookUp(c, n[0], {checkAmbiguity, checkUndeclared})
|
if typeExpr.typ.kind != tyTypeDesc:
|
||||||
if head.kind in {skType}:
|
localError(n.info, errTypeExpected)
|
||||||
var toBind = initIntSet()
|
return errorType(c)
|
||||||
var preprocessed = semGenericStmt(c, n, {}, toBind)
|
result = typeExpr.typ.base
|
||||||
return makeTypeFromExpr(c, preprocessed)
|
if result.isMetaType:
|
||||||
|
var toBind = initIntSet()
|
||||||
|
var preprocessed = semGenericStmt(c, n, {}, toBind)
|
||||||
|
return makeTypeFromExpr(c, preprocessed)
|
||||||
|
of nkIdent, nkAccQuoted:
|
||||||
var s = semTypeIdent(c, n)
|
var s = semTypeIdent(c, n)
|
||||||
if s.typ == nil:
|
if s.typ == nil:
|
||||||
if s.kind != skError: localError(n.info, errTypeExpected)
|
if s.kind != skError: localError(n.info, errTypeExpected)
|
||||||
|
|
@ -1262,6 +1294,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||||
|
|
||||||
if typ == nil:
|
if typ == nil:
|
||||||
typ = newTypeS(tyGenericParam, c)
|
typ = newTypeS(tyGenericParam, c)
|
||||||
|
if father == nil: typ.flags.incl tfWildcard
|
||||||
|
|
||||||
typ.flags.incl tfGenericTypeParam
|
typ.flags.incl tfGenericTypeParam
|
||||||
|
|
||||||
|
|
@ -1272,8 +1305,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||||
# type for each generic param. the index
|
# type for each generic param. the index
|
||||||
# of the parameter will be stored in the
|
# of the parameter will be stored in the
|
||||||
# attached symbol.
|
# attached symbol.
|
||||||
var s = case finalType.kind
|
var s = if finalType.kind == tyStatic or tfWildcard in typ.flags:
|
||||||
of tyStatic:
|
|
||||||
newSymG(skGenericParam, a.sons[j], c).linkTo(finalType)
|
newSymG(skGenericParam, a.sons[j], c).linkTo(finalType)
|
||||||
else:
|
else:
|
||||||
newSymG(skType, a.sons[j], c).linkTo(finalType)
|
newSymG(skType, a.sons[j], c).linkTo(finalType)
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ type
|
||||||
|
|
||||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
|
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
|
||||||
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
|
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
|
||||||
proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode
|
proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode): PNode
|
||||||
|
|
||||||
template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
|
template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
|
||||||
when false:
|
when false:
|
||||||
|
|
@ -96,8 +96,11 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
|
||||||
checkMetaInvariants(cl, result)
|
checkMetaInvariants(cl, result)
|
||||||
|
|
||||||
proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
|
proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
|
||||||
|
let t = replaceTypeVarsT(cl, n.typ)
|
||||||
|
if t != nil and t.kind == tyStatic and t.n != nil:
|
||||||
|
return t.n
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
result.typ = replaceTypeVarsT(cl, n.typ)
|
result.typ = t
|
||||||
if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym)
|
if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym)
|
||||||
let isCall = result.kind in nkCallKinds
|
let isCall = result.kind in nkCallKinds
|
||||||
for i in 0 .. <n.safeLen:
|
for i in 0 .. <n.safeLen:
|
||||||
|
|
@ -197,8 +200,8 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
|
||||||
result = copySym(s, false)
|
result = copySym(s, false)
|
||||||
incl(result.flags, sfFromGeneric)
|
incl(result.flags, sfFromGeneric)
|
||||||
idTablePut(cl.symMap, s, result)
|
idTablePut(cl.symMap, s, result)
|
||||||
result.typ = replaceTypeVarsT(cl, s.typ)
|
|
||||||
result.owner = s.owner
|
result.owner = s.owner
|
||||||
|
result.typ = replaceTypeVarsT(cl, s.typ)
|
||||||
result.ast = replaceTypeVarsN(cl, s.ast)
|
result.ast = replaceTypeVarsN(cl, s.ast)
|
||||||
|
|
||||||
proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
|
proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
|
||||||
|
|
@ -210,7 +213,7 @@ proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
|
||||||
elif result.kind == tyGenericParam and not cl.allowMetaTypes:
|
elif result.kind == tyGenericParam and not cl.allowMetaTypes:
|
||||||
internalError(cl.info, "substitution with generic parameter")
|
internalError(cl.info, "substitution with generic parameter")
|
||||||
|
|
||||||
proc instCopyType(cl: var TReplTypeVars, t: PType): PType =
|
proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# XXX: relying on allowMetaTypes is a kludge
|
# XXX: relying on allowMetaTypes is a kludge
|
||||||
result = copyType(t, t.owner, cl.allowMetaTypes)
|
result = copyType(t, t.owner, cl.allowMetaTypes)
|
||||||
result.flags.incl tfFromGeneric
|
result.flags.incl tfFromGeneric
|
||||||
|
|
@ -281,7 +284,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
rawAddSon(result, newbody)
|
rawAddSon(result, newbody)
|
||||||
checkPartialConstructedType(cl.info, newbody)
|
checkPartialConstructedType(cl.info, newbody)
|
||||||
|
|
||||||
proc eraseVoidParams(t: PType) =
|
proc eraseVoidParams*(t: PType) =
|
||||||
if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
|
if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
|
||||||
t.sons[0] = nil
|
t.sons[0] = nil
|
||||||
|
|
||||||
|
|
@ -298,7 +301,7 @@ proc eraseVoidParams(t: PType) =
|
||||||
setLen t.n.sons, pos
|
setLen t.n.sons, pos
|
||||||
return
|
return
|
||||||
|
|
||||||
proc skipIntLiteralParams(t: PType) =
|
proc skipIntLiteralParams*(t: PType) =
|
||||||
for i in 0 .. <t.sonsLen:
|
for i in 0 .. <t.sonsLen:
|
||||||
let p = t.sons[i]
|
let p = t.sons[i]
|
||||||
if p == nil: continue
|
if p == nil: continue
|
||||||
|
|
@ -395,20 +398,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
|
|
||||||
result.n = replaceTypeVarsN(cl, result.n)
|
result.n = replaceTypeVarsN(cl, result.n)
|
||||||
|
|
||||||
# XXX: This is not really needed?
|
|
||||||
# if result.kind in GenericTypes:
|
|
||||||
# localError(cl.info, errCannotInstantiateX, typeToString(t, preferName))
|
|
||||||
|
|
||||||
case result.kind
|
case result.kind
|
||||||
of tyArray:
|
of tyArray:
|
||||||
let idx = result.sons[0]
|
let idx = result.sons[0]
|
||||||
if idx.kind == tyStatic:
|
internalAssert idx.kind != tyStatic
|
||||||
if idx.n == nil:
|
|
||||||
let lookup = lookupTypeVar(cl, idx)
|
|
||||||
internalAssert lookup != nil
|
|
||||||
idx.n = lookup.n
|
|
||||||
|
|
||||||
result.sons[0] = makeRangeType(cl.c, 0, idx.n.intVal - 1, idx.n.info)
|
|
||||||
|
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
propagateFieldFlags(result, result.n)
|
propagateFieldFlags(result, result.n)
|
||||||
|
|
@ -419,7 +412,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc initTypeVars(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
|
proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
|
||||||
initIdTable(result.symMap)
|
initIdTable(result.symMap)
|
||||||
copyIdTable(result.typeMap, pt)
|
copyIdTable(result.typeMap, pt)
|
||||||
initIdTable(result.localCache)
|
initIdTable(result.localCache)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
|
intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
|
||||||
magicsys, condsyms, idents, lexer, options, parampatterns, strutils
|
magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees
|
||||||
|
|
||||||
when not defined(noDocgen):
|
when not defined(noDocgen):
|
||||||
import docgen
|
import docgen
|
||||||
|
|
@ -40,6 +40,8 @@ type
|
||||||
proxyMatch*: bool # to prevent instantiations
|
proxyMatch*: bool # to prevent instantiations
|
||||||
genericConverter*: bool # true if a generic converter needs to
|
genericConverter*: bool # true if a generic converter needs to
|
||||||
# be instantiated
|
# be instantiated
|
||||||
|
coerceDistincts*: bool # this is an explicit coercion that can strip away
|
||||||
|
# a distrinct type
|
||||||
typedescMatched: bool
|
typedescMatched: bool
|
||||||
inheritancePenalty: int # to prefer closest father object type
|
inheritancePenalty: int # to prefer closest father object type
|
||||||
errors*: seq[string] # additional clarifications to be displayed to the
|
errors*: seq[string] # additional clarifications to be displayed to the
|
||||||
|
|
@ -114,6 +116,9 @@ proc newCandidate*(ctx: PContext, callee: PSym,
|
||||||
binding: PNode, calleeScope = -1): TCandidate =
|
binding: PNode, calleeScope = -1): TCandidate =
|
||||||
initCandidate(ctx, result, callee, binding, calleeScope)
|
initCandidate(ctx, result, callee, binding, calleeScope)
|
||||||
|
|
||||||
|
proc newCandidate*(ctx: PContext, callee: PType): TCandidate =
|
||||||
|
initCandidate(ctx, result, callee)
|
||||||
|
|
||||||
proc copyCandidate(a: var TCandidate, b: TCandidate) =
|
proc copyCandidate(a: var TCandidate, b: TCandidate) =
|
||||||
a.c = b.c
|
a.c = b.c
|
||||||
a.exactMatches = b.exactMatches
|
a.exactMatches = b.exactMatches
|
||||||
|
|
@ -460,7 +465,8 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
|
|
||||||
if param.kind == nkVarTy:
|
if param.kind == nkVarTy:
|
||||||
dummyName = param[0]
|
dummyName = param[0]
|
||||||
dummyType = makeVarType(c, a)
|
dummyType = if a.kind != tyVar: makeVarType(c, a)
|
||||||
|
else: a
|
||||||
else:
|
else:
|
||||||
dummyName = param
|
dummyName = param
|
||||||
dummyType = a
|
dummyType = a
|
||||||
|
|
@ -470,7 +476,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
dummyParam.typ = dummyType
|
dummyParam.typ = dummyType
|
||||||
addDecl(c, dummyParam)
|
addDecl(c, dummyParam)
|
||||||
|
|
||||||
var checkedBody = c.semTryExpr(c, copyTree(body.n[3]), bufferErrors = false)
|
var checkedBody = c.semTryExpr(c, body.n[3].copyTree, bufferErrors = false)
|
||||||
m.errors = bufferedMsgs
|
m.errors = bufferedMsgs
|
||||||
clearBufferedMsgs()
|
clearBufferedMsgs()
|
||||||
if checkedBody == nil: return isNone
|
if checkedBody == nil: return isNone
|
||||||
|
|
@ -485,6 +491,23 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
|
|
||||||
return isGeneric
|
return isGeneric
|
||||||
|
|
||||||
|
proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool =
|
||||||
|
if rules.kind == nkWith:
|
||||||
|
for r in rules:
|
||||||
|
if r.considerAcc == callIdent: return true
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
for r in rules:
|
||||||
|
if r.considerAcc == callIdent: return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc maybeSkipDistinct(t: PType, callee: PSym): PType =
|
||||||
|
if t != nil and t.kind == tyDistinct and t.n != nil and
|
||||||
|
shouldSkipDistinct(t.n, callee.name):
|
||||||
|
result = t.base
|
||||||
|
else:
|
||||||
|
result = t
|
||||||
|
|
||||||
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
# typeRel can be used to establish various relationships between types:
|
# typeRel can be used to establish various relationships between types:
|
||||||
#
|
#
|
||||||
|
|
@ -512,7 +535,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
assert(aOrig != nil)
|
assert(aOrig != nil)
|
||||||
|
|
||||||
# var and static arguments match regular modifier-free types
|
# var and static arguments match regular modifier-free types
|
||||||
let a = aOrig.skipTypes({tyStatic, tyVar})
|
let a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym)
|
||||||
|
# XXX: Theoretically, maybeSkipDistinct could be called before we even
|
||||||
|
# start the param matching process. This could be done in `prepareOperand`
|
||||||
|
# for example, but unfortunately `prepareOperand` is not called in certain
|
||||||
|
# situation when nkDotExpr are rotated to nkDotCalls
|
||||||
|
|
||||||
if a.kind == tyGenericInst and
|
if a.kind == tyGenericInst and
|
||||||
skipTypes(f, {tyVar}).kind notin {
|
skipTypes(f, {tyVar}).kind notin {
|
||||||
|
|
@ -522,8 +549,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
|
|
||||||
template bindingRet(res) =
|
template bindingRet(res) =
|
||||||
when res == isGeneric:
|
when res == isGeneric:
|
||||||
let bound = aOrig.skipTypes({tyRange}).skipIntLit
|
if doBind:
|
||||||
put(c.bindings, f, bound)
|
let bound = aOrig.skipTypes({tyRange}).skipIntLit
|
||||||
|
if doBind: put(c.bindings, f, bound)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
template considerPreviousT(body: stmt) {.immediate.} =
|
template considerPreviousT(body: stmt) {.immediate.} =
|
||||||
|
|
@ -620,8 +648,24 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
else:
|
else:
|
||||||
fRange = prev
|
fRange = prev
|
||||||
result = typeRel(c, f.sons[1], a.sons[1])
|
result = typeRel(c, f.sons[1], a.sons[1])
|
||||||
if result < isGeneric: result = isNone
|
if result < isGeneric:
|
||||||
elif lengthOrd(fRange) != lengthOrd(a): result = isNone
|
result = isNone
|
||||||
|
elif tfUnresolved in fRange.flags and
|
||||||
|
rangeHasStaticIf(fRange):
|
||||||
|
# This is a range from an array instantiated with a generic
|
||||||
|
# static param. We must extract the static param here and bind
|
||||||
|
# it to the size of the currently supplied array.
|
||||||
|
var
|
||||||
|
rangeStaticT = fRange.getStaticTypeFromRange
|
||||||
|
replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
|
||||||
|
inputUpperBound = a.sons[0].n[1].intVal
|
||||||
|
# we must correct for the off-by-one discrepancy between
|
||||||
|
# ranges and static params:
|
||||||
|
replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
|
||||||
|
put(c.bindings, rangeStaticT, replacementT)
|
||||||
|
result = isGeneric
|
||||||
|
elif lengthOrd(fRange) != lengthOrd(a):
|
||||||
|
result = isNone
|
||||||
else: discard
|
else: discard
|
||||||
of tyOpenArray, tyVarargs:
|
of tyOpenArray, tyVarargs:
|
||||||
case a.kind
|
case a.kind
|
||||||
|
|
@ -683,6 +727,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
result = isSubtype
|
result = isSubtype
|
||||||
of tyDistinct:
|
of tyDistinct:
|
||||||
if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual
|
if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual
|
||||||
|
elif c.coerceDistincts: result = typeRel(c, f.base, a)
|
||||||
of tySet:
|
of tySet:
|
||||||
if a.kind == tySet:
|
if a.kind == tySet:
|
||||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
||||||
|
|
@ -845,7 +890,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
|
|
||||||
of tyUserTypeClass, tyUserTypeClassInst:
|
of tyUserTypeClass, tyUserTypeClassInst:
|
||||||
considerPreviousT:
|
considerPreviousT:
|
||||||
result = matchUserTypeClass(c.c, c, f, a)
|
result = matchUserTypeClass(c.c, c, f, aOrig)
|
||||||
if result == isGeneric:
|
if result == isGeneric:
|
||||||
put(c.bindings, f, a)
|
put(c.bindings, f, a)
|
||||||
|
|
||||||
|
|
@ -860,14 +905,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
of tyGenericParam:
|
of tyGenericParam:
|
||||||
var x = PType(idTableGet(c.bindings, f))
|
var x = PType(idTableGet(c.bindings, f))
|
||||||
if x == nil:
|
if x == nil:
|
||||||
if c.calleeSym != nil and c.calleeSym.kind == skType and
|
if c.callee.kind == tyGenericBody and
|
||||||
f.kind == tyGenericParam and not c.typedescMatched:
|
f.kind == tyGenericParam and not c.typedescMatched:
|
||||||
# XXX: The fact that generic types currently use tyGenericParam for
|
# XXX: The fact that generic types currently use tyGenericParam for
|
||||||
# their parameters is really a misnomer. tyGenericParam means "match
|
# their parameters is really a misnomer. tyGenericParam means "match
|
||||||
# any value" and what we need is "match any type", which can be encoded
|
# any value" and what we need is "match any type", which can be encoded
|
||||||
# by a tyTypeDesc params. Unfortunately, this requires more substantial
|
# by a tyTypeDesc params. Unfortunately, this requires more substantial
|
||||||
# changes in semtypinst and elsewhere.
|
# changes in semtypinst and elsewhere.
|
||||||
if a.kind == tyTypeDesc:
|
if tfWildcard in a.flags:
|
||||||
|
result = isGeneric
|
||||||
|
elif a.kind == tyTypeDesc:
|
||||||
if f.sonsLen == 0:
|
if f.sonsLen == 0:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
else:
|
else:
|
||||||
|
|
@ -883,11 +930,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
|
|
||||||
if result == isGeneric:
|
if result == isGeneric:
|
||||||
var concrete = concreteType(c, a)
|
var concrete = a
|
||||||
if concrete == nil:
|
if tfWildcard in a.flags:
|
||||||
result = isNone
|
a.sym.kind = skType
|
||||||
|
a.flags.excl tfWildcard
|
||||||
else:
|
else:
|
||||||
if doBind: put(c.bindings, f, concrete)
|
concrete = concreteType(c, a)
|
||||||
|
if concrete == nil:
|
||||||
|
return isNone
|
||||||
|
if doBind:
|
||||||
|
put(c.bindings, f, concrete)
|
||||||
elif a.kind == tyEmpty:
|
elif a.kind == tyEmpty:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
elif x.kind == tyGenericParam:
|
elif x.kind == tyGenericParam:
|
||||||
|
|
@ -938,7 +990,24 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
of tyProxy:
|
of tyProxy:
|
||||||
result = isEqual
|
result = isEqual
|
||||||
|
|
||||||
else: internalAssert false
|
of tyFromExpr:
|
||||||
|
# fix the expression, so it contains the already instantiated types
|
||||||
|
let instantiated = replaceTypesInBody(c.c, c.bindings, f.n)
|
||||||
|
let reevaluted = c.c.semExpr(c.c, instantiated)
|
||||||
|
case reevaluted.typ.kind
|
||||||
|
of tyTypeDesc:
|
||||||
|
result = typeRel(c, a, reevaluted.typ.base)
|
||||||
|
of tyStatic:
|
||||||
|
result = typeRel(c, a, reevaluted.typ.base)
|
||||||
|
if result != isNone and reevaluted.typ.n != nil:
|
||||||
|
if not exprStructuralEquivalent(aOrig.n, reevaluted.typ.n):
|
||||||
|
result = isNone
|
||||||
|
else:
|
||||||
|
localError(f.n.info, errTypeExpected)
|
||||||
|
result = isNone
|
||||||
|
|
||||||
|
else:
|
||||||
|
internalAssert false
|
||||||
|
|
||||||
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
|
|
@ -1030,12 +1099,26 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
# XXX: When implicit statics are the default
|
# XXX: When implicit statics are the default
|
||||||
# this will be done earlier - we just have to
|
# this will be done earlier - we just have to
|
||||||
# make sure that static types enter here
|
# make sure that static types enter here
|
||||||
var evaluated = c.semTryConstExpr(c, arg)
|
|
||||||
if evaluated != nil:
|
# XXX: weaken tyGenericParam and call it tyGenericPlaceholder
|
||||||
arg.typ = newTypeS(tyStatic, c)
|
# and finally start using tyTypedesc for generic types properly.
|
||||||
arg.typ.sons = @[evaluated.typ]
|
if argType.kind == tyGenericParam and tfWildcard in argType.flags:
|
||||||
arg.typ.n = evaluated
|
argType.assignType(f)
|
||||||
argType = arg.typ
|
# put(m.bindings, f, argType)
|
||||||
|
return argSemantized
|
||||||
|
|
||||||
|
if argType.kind == tyStatic:
|
||||||
|
if m.callee.kind == tyGenericBody:
|
||||||
|
result = newNodeI(nkType, argOrig.info)
|
||||||
|
result.typ = makeTypeFromExpr(c, arg)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
var evaluated = c.semTryConstExpr(c, arg)
|
||||||
|
if evaluated != nil:
|
||||||
|
arg.typ = newTypeS(tyStatic, c)
|
||||||
|
arg.typ.sons = @[evaluated.typ]
|
||||||
|
arg.typ.n = evaluated
|
||||||
|
argType = arg.typ
|
||||||
|
|
||||||
var
|
var
|
||||||
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor})
|
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor})
|
||||||
|
|
|
||||||
|
|
@ -436,6 +436,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
of tyStatic:
|
of tyStatic:
|
||||||
internalAssert t.len > 0
|
internalAssert t.len > 0
|
||||||
result = "static[" & typeToString(t.sons[0]) & "]"
|
result = "static[" & typeToString(t.sons[0]) & "]"
|
||||||
|
if t.n != nil: result.add "(" & renderTree(t.n) & ")"
|
||||||
of tyUserTypeClass:
|
of tyUserTypeClass:
|
||||||
internalAssert t.sym != nil and t.sym.owner != nil
|
internalAssert t.sym != nil and t.sym.owner != nil
|
||||||
return t.sym.owner.name.s
|
return t.sym.owner.name.s
|
||||||
|
|
@ -829,6 +830,12 @@ proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
|
||||||
result = sameTypeOrNilAux(a.sons[i], b.sons[i], c)
|
result = sameTypeOrNilAux(a.sons[i], b.sons[i], c)
|
||||||
if not result: return
|
if not result: return
|
||||||
|
|
||||||
|
proc isGenericAlias*(t: PType): bool =
|
||||||
|
return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst
|
||||||
|
|
||||||
|
proc skipGenericAlias*(t: PType): PType =
|
||||||
|
return if t.isGenericAlias: t.lastSon else: t
|
||||||
|
|
||||||
proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||||
template cycleCheck() =
|
template cycleCheck() =
|
||||||
# believe it or not, the direct check for ``containsOrIncl(c, a, b)``
|
# believe it or not, the direct check for ``containsOrIncl(c, a, b)``
|
||||||
|
|
@ -859,6 +866,19 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||||
of dcEqOrDistinctOf:
|
of dcEqOrDistinctOf:
|
||||||
while a.kind == tyDistinct: a = a.sons[0]
|
while a.kind == tyDistinct: a = a.sons[0]
|
||||||
if a.kind != b.kind: return false
|
if a.kind != b.kind: return false
|
||||||
|
|
||||||
|
if x.kind == tyGenericInst:
|
||||||
|
let
|
||||||
|
lhs = x.skipGenericAlias
|
||||||
|
rhs = y.skipGenericAlias
|
||||||
|
if rhs.kind != tyGenericInst or lhs.base != rhs.base:
|
||||||
|
return false
|
||||||
|
for i in 1 .. lhs.len - 2:
|
||||||
|
let ff = rhs.sons[i]
|
||||||
|
let aa = lhs.sons[i]
|
||||||
|
if not sameTypeAux(ff, aa, c): return false
|
||||||
|
return true
|
||||||
|
|
||||||
case a.kind
|
case a.kind
|
||||||
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
|
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
|
||||||
tyInt..tyBigNum, tyStmt, tyExpr:
|
tyInt..tyBigNum, tyStmt, tyExpr:
|
||||||
|
|
@ -883,8 +903,6 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
cycleCheck()
|
cycleCheck()
|
||||||
result = sameTuple(a, b, c) and sameFlags(a, b)
|
result = sameTuple(a, b, c) and sameFlags(a, b)
|
||||||
of tyGenericInst:
|
|
||||||
result = sameTypeAux(lastSon(a), lastSon(b), c)
|
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
if c.cmp == dcEqIgnoreDistinct: result = false
|
if c.cmp == dcEqIgnoreDistinct: result = false
|
||||||
elif ExactTypeDescValues in c.flags:
|
elif ExactTypeDescValues in c.flags:
|
||||||
|
|
@ -911,6 +929,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||||
result = sameTypeOrNilAux(a.sons[0], b.sons[0], c) and
|
result = sameTypeOrNilAux(a.sons[0], b.sons[0], c) and
|
||||||
sameValue(a.n.sons[0], b.n.sons[0]) and
|
sameValue(a.n.sons[0], b.n.sons[0]) and
|
||||||
sameValue(a.n.sons[1], b.n.sons[1])
|
sameValue(a.n.sons[1], b.n.sons[1])
|
||||||
|
of tyGenericInst: discard
|
||||||
of tyNone: result = false
|
of tyNone: result = false
|
||||||
|
|
||||||
proc sameBackendType*(x, y: PType): bool =
|
proc sameBackendType*(x, y: PType): bool =
|
||||||
|
|
@ -1004,12 +1023,6 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
|
||||||
a = a.sons[i]
|
a = a.sons[i]
|
||||||
result = a.kind == last
|
result = a.kind == last
|
||||||
|
|
||||||
proc isGenericAlias*(t: PType): bool =
|
|
||||||
return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst
|
|
||||||
|
|
||||||
proc skipGenericAlias*(t: PType): PType =
|
|
||||||
return if t.isGenericAlias: t.lastSon else: t
|
|
||||||
|
|
||||||
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
|
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
|
||||||
flags: TTypeAllowedFlags = {}): bool =
|
flags: TTypeAllowedFlags = {}): bool =
|
||||||
assert(kind in {skVar, skLet, skConst, skParam, skResult})
|
assert(kind in {skVar, skLet, skConst, skParam, skResult})
|
||||||
|
|
@ -1255,15 +1268,18 @@ proc getSize(typ: PType): BiggestInt =
|
||||||
if result < 0: internalError("getSize: " & $typ.kind)
|
if result < 0: internalError("getSize: " & $typ.kind)
|
||||||
|
|
||||||
proc containsGenericTypeIter(t: PType, closure: PObject): bool =
|
proc containsGenericTypeIter(t: PType, closure: PObject): bool =
|
||||||
if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}:
|
if t.kind == tyStatic:
|
||||||
return true
|
return t.n == nil
|
||||||
|
|
||||||
if t.kind == tyTypeDesc:
|
if t.kind == tyTypeDesc:
|
||||||
if t.base.kind == tyNone: return true
|
if t.base.kind == tyNone: return true
|
||||||
if containsGenericTypeIter(t.base, closure): return true
|
if containsGenericTypeIter(t.base, closure): return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
return t.kind == tyStatic and t.n == nil
|
if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}:
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
proc containsGenericType*(t: PType): bool =
|
proc containsGenericType*(t: PType): bool =
|
||||||
result = iterOverType(t, containsGenericTypeIter, nil)
|
result = iterOverType(t, containsGenericTypeIter, nil)
|
||||||
|
|
|
||||||
|
|
@ -1570,7 +1570,7 @@ proc genProc(c: PCtx; s: PSym): int =
|
||||||
c.gABC(body, opcEof, eofInstr.regA)
|
c.gABC(body, opcEof, eofInstr.regA)
|
||||||
c.optimizeJumps(result)
|
c.optimizeJumps(result)
|
||||||
s.offset = c.prc.maxSlots
|
s.offset = c.prc.maxSlots
|
||||||
#if s.name.s == "xmlConstructor":
|
#if s.name.s == "foo":
|
||||||
# echo renderTree(body)
|
# echo renderTree(body)
|
||||||
# c.echoCode(result)
|
# c.echoCode(result)
|
||||||
c.prc = oldPrc
|
c.prc = oldPrc
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ type
|
||||||
wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast,
|
wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast,
|
||||||
wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch,
|
wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch,
|
||||||
wThis, wThrow, wTrue, wTypedef, wTypeid, wTypename,
|
wThis, wThrow, wTrue, wTypedef, wTypeid, wTypename,
|
||||||
wUnion, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t,
|
wUnion, wPacked, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t,
|
||||||
|
|
||||||
wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept,
|
wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept,
|
||||||
wThread_local, wStatic_assert, wChar16_t, wChar32_t,
|
wThread_local, wStatic_assert, wChar16_t, wChar32_t,
|
||||||
|
|
@ -155,7 +155,7 @@ const
|
||||||
"private", "protected", "public", "register", "reinterpret_cast",
|
"private", "protected", "public", "register", "reinterpret_cast",
|
||||||
"short", "signed", "sizeof", "static_cast", "struct", "switch",
|
"short", "signed", "sizeof", "static_cast", "struct", "switch",
|
||||||
"this", "throw", "true", "typedef", "typeid",
|
"this", "throw", "true", "typedef", "typeid",
|
||||||
"typename", "union", "unsigned", "virtual", "void", "volatile",
|
"typename", "union", "packed", "unsigned", "virtual", "void", "volatile",
|
||||||
"wchar_t",
|
"wchar_t",
|
||||||
|
|
||||||
"alignas", "alignof", "constexpr", "decltype", "nullptr", "noexcept",
|
"alignas", "alignof", "constexpr", "decltype", "nullptr", "noexcept",
|
||||||
|
|
|
||||||
|
|
@ -5395,6 +5395,16 @@ checked.
|
||||||
**Future directions**: GC'ed memory should be allowed in unions and the GC
|
**Future directions**: GC'ed memory should be allowed in unions and the GC
|
||||||
should scan unions conservatively.
|
should scan unions conservatively.
|
||||||
|
|
||||||
|
Packed pragma
|
||||||
|
-------------
|
||||||
|
The `packed`:idx: pragma can be applied to any ``object`` type. It ensures
|
||||||
|
that the fields of an object are packed back-to-back in memory. It is useful
|
||||||
|
to store packets or messages from/to network or hardware drivers, and for
|
||||||
|
interoperability with C. Combining packed pragma with inheritance is not
|
||||||
|
defined, and it should not be used with GC'ed memory (ref's).
|
||||||
|
|
||||||
|
**Future directions**: Using GC'ed memory in packed pragma will result in
|
||||||
|
compile-time error. Usage with inheritance should be defined and documented.
|
||||||
|
|
||||||
Unchecked pragma
|
Unchecked pragma
|
||||||
----------------
|
----------------
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,14 @@ proc complete*[T](future: PFuture[T], val: T) =
|
||||||
if future.cb != nil:
|
if future.cb != nil:
|
||||||
future.cb()
|
future.cb()
|
||||||
|
|
||||||
|
proc complete*(future: PFuture[void]) =
|
||||||
|
## Completes a void ``future``.
|
||||||
|
assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||||
|
assert(future.error == nil)
|
||||||
|
future.finished = true
|
||||||
|
if future.cb != nil:
|
||||||
|
future.cb()
|
||||||
|
|
||||||
proc fail*[T](future: PFuture[T], error: ref EBase) =
|
proc fail*[T](future: PFuture[T], error: ref EBase) =
|
||||||
## Completes ``future`` with ``error``.
|
## Completes ``future`` with ``error``.
|
||||||
assert(not future.finished, "Future already finished, cannot finish twice.")
|
assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||||
|
|
@ -76,7 +84,8 @@ proc read*[T](future: PFuture[T]): T =
|
||||||
## If the result of the future is an error then that error will be raised.
|
## If the result of the future is an error then that error will be raised.
|
||||||
if future.finished:
|
if future.finished:
|
||||||
if future.error != nil: raise future.error
|
if future.error != nil: raise future.error
|
||||||
return future.value
|
when T isnot void:
|
||||||
|
return future.value
|
||||||
else:
|
else:
|
||||||
# TODO: Make a custom exception type for this?
|
# TODO: Make a custom exception type for this?
|
||||||
raise newException(EInvalidValue, "Future still in progress.")
|
raise newException(EInvalidValue, "Future still in progress.")
|
||||||
|
|
@ -132,7 +141,6 @@ when defined(windows) or defined(nimdoc):
|
||||||
cast[TCompletionKey](sock), 1) == 0:
|
cast[TCompletionKey](sock), 1) == 0:
|
||||||
OSError(OSLastError())
|
OSError(OSLastError())
|
||||||
p.handles.incl(sock)
|
p.handles.incl(sock)
|
||||||
# TODO: fd closure detection, we need to remove the fd from handles set
|
|
||||||
|
|
||||||
proc verifyPresence(p: PDispatcher, sock: TSocketHandle) =
|
proc verifyPresence(p: PDispatcher, sock: TSocketHandle) =
|
||||||
## Ensures that socket has been registered with the dispatcher.
|
## Ensures that socket has been registered with the dispatcher.
|
||||||
|
|
@ -244,13 +252,13 @@ when defined(windows) or defined(nimdoc):
|
||||||
RemoteSockaddr, RemoteSockaddrLength)
|
RemoteSockaddr, RemoteSockaddrLength)
|
||||||
|
|
||||||
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
|
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
|
||||||
af = AF_INET): PFuture[int] =
|
af = AF_INET): PFuture[void] =
|
||||||
## Connects ``socket`` to server at ``address:port``.
|
## Connects ``socket`` to server at ``address:port``.
|
||||||
##
|
##
|
||||||
## Returns a ``PFuture`` which will complete when the connection succeeds
|
## Returns a ``PFuture`` which will complete when the connection succeeds
|
||||||
## or an error occurs.
|
## or an error occurs.
|
||||||
verifyPresence(p, socket)
|
verifyPresence(p, socket)
|
||||||
var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed.
|
var retFuture = newFuture[void]()
|
||||||
# Apparently ``ConnectEx`` expects the socket to be initially bound:
|
# Apparently ``ConnectEx`` expects the socket to be initially bound:
|
||||||
var saddr: Tsockaddr_in
|
var saddr: Tsockaddr_in
|
||||||
saddr.sin_family = int16(toInt(af))
|
saddr.sin_family = int16(toInt(af))
|
||||||
|
|
@ -272,7 +280,7 @@ when defined(windows) or defined(nimdoc):
|
||||||
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
|
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||||
if not retFuture.finished:
|
if not retFuture.finished:
|
||||||
if errcode == TOSErrorCode(-1):
|
if errcode == TOSErrorCode(-1):
|
||||||
retFuture.complete(0)
|
retFuture.complete()
|
||||||
else:
|
else:
|
||||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||||
)
|
)
|
||||||
|
|
@ -282,7 +290,7 @@ when defined(windows) or defined(nimdoc):
|
||||||
if ret:
|
if ret:
|
||||||
# Request to connect completed immediately.
|
# Request to connect completed immediately.
|
||||||
success = true
|
success = true
|
||||||
retFuture.complete(0)
|
retFuture.complete()
|
||||||
# We don't deallocate ``ol`` here because even though this completed
|
# We don't deallocate ``ol`` here because even though this completed
|
||||||
# immediately poll will still be notified about its completion and it will
|
# immediately poll will still be notified about its completion and it will
|
||||||
# free ``ol``.
|
# free ``ol``.
|
||||||
|
|
@ -363,11 +371,11 @@ when defined(windows) or defined(nimdoc):
|
||||||
# free ``ol``.
|
# free ``ol``.
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
|
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[void] =
|
||||||
## Sends ``data`` to ``socket``. The returned future will complete once all
|
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||||
## data has been sent.
|
## data has been sent.
|
||||||
verifyPresence(p, socket)
|
verifyPresence(p, socket)
|
||||||
var retFuture = newFuture[int]()
|
var retFuture = newFuture[void]()
|
||||||
|
|
||||||
var dataBuf: TWSABuf
|
var dataBuf: TWSABuf
|
||||||
dataBuf.buf = data
|
dataBuf.buf = data
|
||||||
|
|
@ -379,7 +387,7 @@ when defined(windows) or defined(nimdoc):
|
||||||
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
|
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||||
if not retFuture.finished:
|
if not retFuture.finished:
|
||||||
if errcode == TOSErrorCode(-1):
|
if errcode == TOSErrorCode(-1):
|
||||||
retFuture.complete(0)
|
retFuture.complete()
|
||||||
else:
|
else:
|
||||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||||
)
|
)
|
||||||
|
|
@ -392,7 +400,7 @@ when defined(windows) or defined(nimdoc):
|
||||||
retFuture.fail(newException(EOS, osErrorMsg(err)))
|
retFuture.fail(newException(EOS, osErrorMsg(err)))
|
||||||
dealloc(ol)
|
dealloc(ol)
|
||||||
else:
|
else:
|
||||||
retFuture.complete(0)
|
retFuture.complete()
|
||||||
# We don't deallocate ``ol`` here because even though this completed
|
# We don't deallocate ``ol`` here because even though this completed
|
||||||
# immediately poll will still be notified about its completion and it will
|
# immediately poll will still be notified about its completion and it will
|
||||||
# free ``ol``.
|
# free ``ol``.
|
||||||
|
|
@ -475,6 +483,11 @@ when defined(windows) or defined(nimdoc):
|
||||||
result = socket(domain, typ, protocol)
|
result = socket(domain, typ, protocol)
|
||||||
disp.register(result)
|
disp.register(result)
|
||||||
|
|
||||||
|
proc close*(disp: PDispatcher, socket: TSocketHandle) =
|
||||||
|
## Closes a socket and ensures that it is unregistered.
|
||||||
|
socket.close()
|
||||||
|
disp.handles.excl(socket)
|
||||||
|
|
||||||
initAll()
|
initAll()
|
||||||
else:
|
else:
|
||||||
import selectors
|
import selectors
|
||||||
|
|
@ -508,6 +521,10 @@ else:
|
||||||
result = socket(domain, typ, protocol)
|
result = socket(domain, typ, protocol)
|
||||||
disp.register(result)
|
disp.register(result)
|
||||||
|
|
||||||
|
proc close*(disp: PDispatcher, sock: TSocketHandle) =
|
||||||
|
sock.close()
|
||||||
|
disp.selector.unregister(sock)
|
||||||
|
|
||||||
proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) =
|
proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) =
|
||||||
if sock notin p.selector:
|
if sock notin p.selector:
|
||||||
raise newException(EInvalidValue, "File descriptor not registered.")
|
raise newException(EInvalidValue, "File descriptor not registered.")
|
||||||
|
|
@ -556,12 +573,12 @@ else:
|
||||||
# (e.g. socket disconnected).
|
# (e.g. socket disconnected).
|
||||||
|
|
||||||
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
|
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
|
||||||
af = AF_INET): PFuture[int] =
|
af = AF_INET): PFuture[void] =
|
||||||
var retFuture = newFuture[int]()
|
var retFuture = newFuture[void]()
|
||||||
|
|
||||||
proc cb(sock: TSocketHandle): bool =
|
proc cb(sock: TSocketHandle): bool =
|
||||||
# We have connected.
|
# We have connected.
|
||||||
retFuture.complete(0)
|
retFuture.complete()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
var aiList = getAddrInfo(address, port, af)
|
var aiList = getAddrInfo(address, port, af)
|
||||||
|
|
@ -573,7 +590,7 @@ else:
|
||||||
if ret == 0:
|
if ret == 0:
|
||||||
# Request to connect completed immediately.
|
# Request to connect completed immediately.
|
||||||
success = true
|
success = true
|
||||||
retFuture.complete(0)
|
retFuture.complete()
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
lastError = osLastError()
|
lastError = osLastError()
|
||||||
|
|
@ -627,8 +644,8 @@ else:
|
||||||
addRead(p, socket, cb)
|
addRead(p, socket, cb)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
|
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[void] =
|
||||||
var retFuture = newFuture[int]()
|
var retFuture = newFuture[void]()
|
||||||
|
|
||||||
var written = 0
|
var written = 0
|
||||||
|
|
||||||
|
|
@ -648,7 +665,7 @@ else:
|
||||||
if res != netSize:
|
if res != netSize:
|
||||||
result = false # We still have data to send.
|
result = false # We still have data to send.
|
||||||
else:
|
else:
|
||||||
retFuture.complete(0)
|
retFuture.complete()
|
||||||
addWrite(p, socket, cb)
|
addWrite(p, socket, cb)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
|
|
@ -781,12 +798,17 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||||
|
|
||||||
hint("Processing " & prc[0].getName & " as an async proc.")
|
hint("Processing " & prc[0].getName & " as an async proc.")
|
||||||
|
|
||||||
|
let returnType = prc[3][0]
|
||||||
|
var subtypeName = ""
|
||||||
# Verify that the return type is a PFuture[T]
|
# Verify that the return type is a PFuture[T]
|
||||||
if prc[3][0].kind == nnkIdent:
|
if returnType.kind == nnkIdent:
|
||||||
error("Expected return type of 'PFuture' got '" & $prc[3][0] & "'")
|
error("Expected return type of 'PFuture' got '" & $returnType & "'")
|
||||||
elif prc[3][0].kind == nnkBracketExpr:
|
elif returnType.kind == nnkBracketExpr:
|
||||||
if $prc[3][0][0] != "PFuture":
|
if $returnType[0] != "PFuture":
|
||||||
error("Expected return type of 'PFuture' got '" & $prc[3][0][0] & "'")
|
error("Expected return type of 'PFuture' got '" & $returnType[0] & "'")
|
||||||
|
subtypeName = $returnType[1].ident
|
||||||
|
elif returnType.kind == nnkEmpty:
|
||||||
|
subtypeName = "void"
|
||||||
|
|
||||||
# TODO: Why can't I use genSym? I get illegal capture errors for Syms.
|
# TODO: Why can't I use genSym? I get illegal capture errors for Syms.
|
||||||
# TODO: It seems genSym is broken. Change all usages back to genSym when fixed
|
# TODO: It seems genSym is broken. Change all usages back to genSym when fixed
|
||||||
|
|
@ -799,20 +821,24 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||||
newVarStmt(retFutureSym,
|
newVarStmt(retFutureSym,
|
||||||
newCall(
|
newCall(
|
||||||
newNimNode(nnkBracketExpr).add(
|
newNimNode(nnkBracketExpr).add(
|
||||||
newIdentNode("newFuture"),
|
newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
|
||||||
prc[3][0][1])))) # Get type from return type of this proc.
|
newIdentNode(subtypeName))))) # Get type from return type of this proc
|
||||||
|
echo(treeRepr(outerProcBody))
|
||||||
# -> iterator nameIter(): PFutureBase {.closure.} =
|
# -> iterator nameIter(): PFutureBase {.closure.} =
|
||||||
# -> var result: T
|
# -> var result: T
|
||||||
# -> <proc_body>
|
# -> <proc_body>
|
||||||
# -> complete(retFuture, result)
|
# -> complete(retFuture, result)
|
||||||
var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter")
|
var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter")
|
||||||
var procBody = prc[6].processBody(retFutureSym)
|
var procBody = prc[6].processBody(retFutureSym)
|
||||||
procBody.insert(0, newNimNode(nnkVarSection).add(
|
if subtypeName != "void":
|
||||||
newIdentDefs(newIdentNode("result"), prc[3][0][1]))) # -> var result: T
|
procBody.insert(0, newNimNode(nnkVarSection).add(
|
||||||
procBody.add(
|
newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T
|
||||||
newCall(newIdentNode("complete"),
|
procBody.add(
|
||||||
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
|
newCall(newIdentNode("complete"),
|
||||||
|
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
|
||||||
|
else:
|
||||||
|
# -> complete(retFuture)
|
||||||
|
procBody.add(newCall(newIdentNode("complete"), retFutureSym))
|
||||||
|
|
||||||
var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")],
|
var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")],
|
||||||
procBody, nnkIteratorDef)
|
procBody, nnkIteratorDef)
|
||||||
|
|
@ -847,6 +873,12 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||||
for i in 0 .. <result[4].len:
|
for i in 0 .. <result[4].len:
|
||||||
if result[4][i].ident == !"async":
|
if result[4][i].ident == !"async":
|
||||||
result[4].del(i)
|
result[4].del(i)
|
||||||
|
if subtypeName == "void":
|
||||||
|
# Add discardable pragma.
|
||||||
|
result[4].add(newIdentNode("discardable"))
|
||||||
|
if returnType.kind == nnkEmpty:
|
||||||
|
# Add PFuture[void]
|
||||||
|
result[3][0] = parseExpr("PFuture[void]")
|
||||||
|
|
||||||
result[6] = outerProcBody
|
result[6] = outerProcBody
|
||||||
|
|
||||||
|
|
@ -892,7 +924,7 @@ proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.}
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
|
||||||
var p = newDispatcher()
|
var p = newDispatcher()
|
||||||
var sock = socket()
|
var sock = p.socket()
|
||||||
sock.setBlocking false
|
sock.setBlocking false
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
259
lib/pure/net.nim
259
lib/pure/net.nim
|
|
@ -9,7 +9,264 @@
|
||||||
|
|
||||||
## This module implements a high-level cross-platform sockets interface.
|
## This module implements a high-level cross-platform sockets interface.
|
||||||
|
|
||||||
import sockets2, os
|
import sockets2, os, strutils, unsigned
|
||||||
|
|
||||||
|
type
|
||||||
|
IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
|
||||||
|
IPv6, ## IPv6 address
|
||||||
|
IPv4 ## IPv4 address
|
||||||
|
|
||||||
|
TIpAddress* = object ## stores an arbitrary IP address
|
||||||
|
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
|
||||||
|
of IpAddressFamily.IPv6:
|
||||||
|
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in case of IPv6
|
||||||
|
of IpAddressFamily.IPv4:
|
||||||
|
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in case of IPv4
|
||||||
|
|
||||||
|
proc IPv4_any*(): TIpAddress =
|
||||||
|
## Returns the IPv4 any address, which can be used to listen on all available
|
||||||
|
## network adapters
|
||||||
|
result = TIpAddress(
|
||||||
|
family: IpAddressFamily.IPv4,
|
||||||
|
address_v4: [0'u8, 0'u8, 0'u8, 0'u8])
|
||||||
|
|
||||||
|
proc IPv4_loopback*(): TIpAddress =
|
||||||
|
## Returns the IPv4 loopback address (127.0.0.1)
|
||||||
|
result = TIpAddress(
|
||||||
|
family: IpAddressFamily.IPv4,
|
||||||
|
address_v4: [127'u8, 0'u8, 0'u8, 1'u8])
|
||||||
|
|
||||||
|
proc IPv4_broadcast*(): TIpAddress =
|
||||||
|
## Returns the IPv4 broadcast address (255.255.255.255)
|
||||||
|
result = TIpAddress(
|
||||||
|
family: IpAddressFamily.IPv4,
|
||||||
|
address_v4: [255'u8, 255'u8, 255'u8, 255'u8])
|
||||||
|
|
||||||
|
proc IPv6_any*(): TIpAddress =
|
||||||
|
## Returns the IPv6 any address (::0), which can be used
|
||||||
|
## to listen on all available network adapters
|
||||||
|
result = TIpAddress(
|
||||||
|
family: IpAddressFamily.IPv6,
|
||||||
|
address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8])
|
||||||
|
|
||||||
|
proc IPv6_loopback*(): TIpAddress =
|
||||||
|
## Returns the IPv6 loopback address (::1)
|
||||||
|
result = TIpAddress(
|
||||||
|
family: IpAddressFamily.IPv6,
|
||||||
|
address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,1'u8])
|
||||||
|
|
||||||
|
proc `==`*(lhs, rhs: TIpAddress): bool =
|
||||||
|
## Compares two IpAddresses for Equality. Returns two if the addresses are equal
|
||||||
|
if lhs.family != rhs.family: return false
|
||||||
|
if lhs.family == IpAddressFamily.IPv4:
|
||||||
|
for i in low(lhs.address_v4) .. high(lhs.address_v4):
|
||||||
|
if lhs.address_v4[i] != rhs.address_v4[i]: return false
|
||||||
|
else: # IPv6
|
||||||
|
for i in low(lhs.address_v6) .. high(lhs.address_v6):
|
||||||
|
if lhs.address_v6[i] != rhs.address_v6[i]: return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc `$`*(address: TIpAddress): string =
|
||||||
|
## Converts an TIpAddress into the textual representation
|
||||||
|
result = ""
|
||||||
|
case address.family
|
||||||
|
of IpAddressFamily.IPv4:
|
||||||
|
for i in 0 .. 3:
|
||||||
|
if i != 0:
|
||||||
|
result.add('.')
|
||||||
|
result.add($address.address_v4[i])
|
||||||
|
of IpAddressFamily.IPv6:
|
||||||
|
var
|
||||||
|
currentZeroStart = -1
|
||||||
|
currentZeroCount = 0
|
||||||
|
biggestZeroStart = -1
|
||||||
|
biggestZeroCount = 0
|
||||||
|
# Look for the largest block of zeros
|
||||||
|
for i in 0..7:
|
||||||
|
var isZero = address.address_v6[i*2] == 0 and address.address_v6[i*2+1] == 0
|
||||||
|
if isZero:
|
||||||
|
if currentZeroStart == -1:
|
||||||
|
currentZeroStart = i
|
||||||
|
currentZeroCount = 1
|
||||||
|
else:
|
||||||
|
currentZeroCount.inc()
|
||||||
|
if currentZeroCount > biggestZeroCount:
|
||||||
|
biggestZeroCount = currentZeroCount
|
||||||
|
biggestZeroStart = currentZeroStart
|
||||||
|
else:
|
||||||
|
currentZeroStart = -1
|
||||||
|
|
||||||
|
if biggestZeroCount == 8: # Special case ::0
|
||||||
|
result.add("::")
|
||||||
|
else: # Print address
|
||||||
|
var printedLastGroup = false
|
||||||
|
for i in 0..7:
|
||||||
|
var word:uint16 = (cast[uint16](address.address_v6[i*2])) shl 8
|
||||||
|
word = word or cast[uint16](address.address_v6[i*2+1])
|
||||||
|
|
||||||
|
if biggestZeroCount != 0 and # Check if group is in skip group
|
||||||
|
(i >= biggestZeroStart and i < (biggestZeroStart + biggestZeroCount)):
|
||||||
|
if i == biggestZeroStart: # skip start
|
||||||
|
result.add("::")
|
||||||
|
printedLastGroup = false
|
||||||
|
else:
|
||||||
|
if printedLastGroup:
|
||||||
|
result.add(':')
|
||||||
|
var
|
||||||
|
afterLeadingZeros = false
|
||||||
|
mask = 0xF000'u16
|
||||||
|
for j in 0'u16..3'u16:
|
||||||
|
var val = (mask and word) shr (4'u16*(3'u16-j))
|
||||||
|
if val != 0 or afterLeadingZeros:
|
||||||
|
if val < 0xA:
|
||||||
|
result.add(chr(uint16(ord('0'))+val))
|
||||||
|
else: # val >= 0xA
|
||||||
|
result.add(chr(uint16(ord('a'))+val-0xA))
|
||||||
|
afterLeadingZeros = true
|
||||||
|
mask = mask shr 4
|
||||||
|
printedLastGroup = true
|
||||||
|
|
||||||
|
proc parseIPv4Address(address_str: string): TIpAddress =
|
||||||
|
## Parses IPv4 adresses
|
||||||
|
## Raises EInvalidValue on errors
|
||||||
|
var
|
||||||
|
byteCount = 0
|
||||||
|
currentByte:uint16 = 0
|
||||||
|
seperatorValid = false
|
||||||
|
|
||||||
|
result.family = IpAddressFamily.IPv4
|
||||||
|
|
||||||
|
for i in 0 .. high(address_str):
|
||||||
|
if address_str[i] in strutils.Digits: # Character is a number
|
||||||
|
currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0'))
|
||||||
|
if currentByte > 255'u16:
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address. Value is out of range")
|
||||||
|
seperatorValid = true
|
||||||
|
elif address_str[i] == '.': # IPv4 address separator
|
||||||
|
if not seperatorValid or byteCount >= 3:
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups")
|
||||||
|
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||||
|
currentByte = 0
|
||||||
|
byteCount.inc
|
||||||
|
seperatorValid = false
|
||||||
|
else:
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character")
|
||||||
|
|
||||||
|
if byteCount != 3 or not seperatorValid:
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address")
|
||||||
|
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||||
|
|
||||||
|
proc parseIPv6Address(address_str: string): TIpAddress =
|
||||||
|
## Parses IPv6 adresses
|
||||||
|
## Raises EInvalidValue on errors
|
||||||
|
result.family = IpAddressFamily.IPv6
|
||||||
|
if address_str.len < 2: raise newException(EInvalidValue, "Invalid IP Address")
|
||||||
|
|
||||||
|
var
|
||||||
|
groupCount = 0
|
||||||
|
currentGroupStart = 0
|
||||||
|
currentShort:uint32 = 0
|
||||||
|
seperatorValid = true
|
||||||
|
dualColonGroup = -1
|
||||||
|
lastWasColon = false
|
||||||
|
v4StartPos = -1
|
||||||
|
byteCount = 0
|
||||||
|
|
||||||
|
for i,c in address_str:
|
||||||
|
if c == ':':
|
||||||
|
if not seperatorValid: raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid seperator")
|
||||||
|
if lastWasColon:
|
||||||
|
if dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address. Address contains more than one \"::\" seperator")
|
||||||
|
dualColonGroup = groupCount
|
||||||
|
seperatorValid = false
|
||||||
|
elif i != 0 and i != high(address_str):
|
||||||
|
if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups")
|
||||||
|
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
|
||||||
|
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
|
||||||
|
currentShort = 0
|
||||||
|
groupCount.inc()
|
||||||
|
if dualColonGroup != -1: seperatorValid = false
|
||||||
|
elif i == 0: # only valid if address starts with ::
|
||||||
|
if address_str[1] != ':':
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address. Address may not start with \":\"")
|
||||||
|
else: # i == high(address_str) - only valid if address ends with ::
|
||||||
|
if address_str[high(address_str)-1] != ':':
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address. Address may not end with \":\"")
|
||||||
|
lastWasColon = true
|
||||||
|
currentGroupStart = i + 1
|
||||||
|
elif c == '.': # Switch to parse IPv4 mode
|
||||||
|
if i < 3 or not seperatorValid or groupCount >= 7: raise newException(EInvalidValue, "Invalid IP Address")
|
||||||
|
v4StartPos = currentGroupStart
|
||||||
|
currentShort = 0
|
||||||
|
seperatorValid = false
|
||||||
|
break
|
||||||
|
elif c in strutils.HexDigits:
|
||||||
|
if c in strutils.Digits: # Normal digit
|
||||||
|
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0'))
|
||||||
|
elif c >= 'a' and c <= 'f': # Lower case hex
|
||||||
|
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10
|
||||||
|
else: # Upper case hex
|
||||||
|
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
|
||||||
|
if currentShort > 65535'u32:
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address. Value is out of range")
|
||||||
|
lastWasColon = false
|
||||||
|
seperatorValid = true
|
||||||
|
else:
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character")
|
||||||
|
|
||||||
|
|
||||||
|
if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
|
||||||
|
if seperatorValid: # Copy remaining data
|
||||||
|
if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups")
|
||||||
|
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
|
||||||
|
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
|
||||||
|
groupCount.inc()
|
||||||
|
else: # Must parse IPv4 address
|
||||||
|
for i,c in address_str[v4StartPos..high(address_str)]:
|
||||||
|
if c in strutils.Digits: # Character is a number
|
||||||
|
currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
|
||||||
|
if currentShort > 255'u32:
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address. Value is out of range")
|
||||||
|
seperatorValid = true
|
||||||
|
elif c == '.': # IPv4 address separator
|
||||||
|
if not seperatorValid or byteCount >= 3:
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address")
|
||||||
|
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
|
||||||
|
currentShort = 0
|
||||||
|
byteCount.inc()
|
||||||
|
seperatorValid = false
|
||||||
|
else: # Invalid character
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character")
|
||||||
|
|
||||||
|
if byteCount != 3 or not seperatorValid:
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address")
|
||||||
|
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
|
||||||
|
groupCount += 2
|
||||||
|
|
||||||
|
# Shift and fill zeros in case of ::
|
||||||
|
if groupCount > 8:
|
||||||
|
raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups")
|
||||||
|
elif groupCount < 8: # must fill
|
||||||
|
if dualColonGroup == -1: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too few groups")
|
||||||
|
var toFill = 8 - groupCount # The number of groups to fill
|
||||||
|
var toShift = groupCount - dualColonGroup # Nr of known groups after ::
|
||||||
|
for i in 0..2*toShift-1: # shift
|
||||||
|
result.address_v6[15-i] = result.address_v6[groupCount*2-i-1]
|
||||||
|
for i in 0..2*toFill-1: # fill with 0s
|
||||||
|
result.address_v6[dualColonGroup*2+i] = 0
|
||||||
|
elif dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups")
|
||||||
|
|
||||||
|
|
||||||
|
proc parseIpAddress*(address_str: string): TIpAddress =
|
||||||
|
## Parses an IP address
|
||||||
|
## Raises EInvalidValue on error
|
||||||
|
if address_str == nil:
|
||||||
|
raise newException(EInvalidValue, "IP Address string is nil")
|
||||||
|
if address_str.contains(':'):
|
||||||
|
return parseIPv6Address(address_str)
|
||||||
|
else:
|
||||||
|
return parseIPv4Address(address_str)
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TSocket* = TSocketHandle
|
TSocket* = TSocketHandle
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ when defined(linux) or defined(nimdoc):
|
||||||
events: set[TEvent]): PSelectorKey {.discardable.} =
|
events: set[TEvent]): PSelectorKey {.discardable.} =
|
||||||
## Updates the events which ``fd`` wants notifications for.
|
## Updates the events which ``fd`` wants notifications for.
|
||||||
if s.fds[fd].events != events:
|
if s.fds[fd].events != events:
|
||||||
echo("Update ", fd.cint, " to ", events)
|
|
||||||
var event = createEventStruct(events, fd)
|
var event = createEventStruct(events, fd)
|
||||||
|
|
||||||
s.fds[fd].events = events
|
s.fds[fd].events = events
|
||||||
|
|
@ -70,7 +69,9 @@ when defined(linux) or defined(nimdoc):
|
||||||
|
|
||||||
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
||||||
OSError(OSLastError())
|
let err = OSLastError()
|
||||||
|
if err.cint notin {ENOENT, EBADF}: # TODO: Why do we sometimes get an EBADF? Is this normal?
|
||||||
|
OSError(err)
|
||||||
result = s.fds[fd]
|
result = s.fds[fd]
|
||||||
s.fds.del(fd)
|
s.fds.del(fd)
|
||||||
|
|
||||||
|
|
@ -78,6 +79,15 @@ when defined(linux) or defined(nimdoc):
|
||||||
if s.epollFD.close() != 0: OSError(OSLastError())
|
if s.epollFD.close() != 0: OSError(OSLastError())
|
||||||
dealloc(addr s.events) # TODO: Test this
|
dealloc(addr s.events) # TODO: Test this
|
||||||
|
|
||||||
|
proc epollHasFd(s: PSelector, fd: TSocketHandle): bool =
|
||||||
|
result = true
|
||||||
|
var event = createEventStruct(s.fds[fd].events, fd)
|
||||||
|
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
||||||
|
let err = osLastError()
|
||||||
|
if err.cint in {ENOENT, EBADF}:
|
||||||
|
return false
|
||||||
|
OSError(OSLastError())
|
||||||
|
|
||||||
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
||||||
##
|
##
|
||||||
## The ``events`` field of the returned ``key`` contains the original events
|
## The ``events`` field of the returned ``key`` contains the original events
|
||||||
|
|
@ -85,24 +95,19 @@ when defined(linux) or defined(nimdoc):
|
||||||
## of the ``TReadyInfo`` tuple which determines which events are ready
|
## of the ``TReadyInfo`` tuple which determines which events are ready
|
||||||
## on the ``fd``.
|
## on the ``fd``.
|
||||||
result = @[]
|
result = @[]
|
||||||
|
|
||||||
let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
|
let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
|
||||||
if evNum < 0: OSError(OSLastError())
|
if evNum < 0: OSError(OSLastError())
|
||||||
if evNum == 0: return @[]
|
if evNum == 0: return @[]
|
||||||
for i in 0 .. <evNum:
|
for i in 0 .. <evNum:
|
||||||
|
let fd = s.events[i].data.fd.TSocketHandle
|
||||||
|
|
||||||
var evSet: set[TEvent] = {}
|
var evSet: set[TEvent] = {}
|
||||||
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
||||||
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
||||||
let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle]
|
let selectorKey = s.fds[fd]
|
||||||
assert selectorKey != nil
|
assert selectorKey != nil
|
||||||
result.add((selectorKey, evSet))
|
result.add((selectorKey, evSet))
|
||||||
|
|
||||||
if (s.events[i].events and EPOLLHUP) != 0 or
|
|
||||||
(s.events[i].events and EPOLLRDHUP) != 0:
|
|
||||||
# fd closed
|
|
||||||
#echo("fd closed ", s.events[i].data.fd)
|
|
||||||
s.unregister(s.events[i].data.fd.TSocketHandle)
|
|
||||||
|
|
||||||
#echo("Epoll: ", result[i].key.fd, " ", result[i].events, " ", result[i].key.events)
|
#echo("Epoll: ", result[i].key.fd, " ", result[i].events, " ", result[i].key.events)
|
||||||
|
|
||||||
proc newSelector*(): PSelector =
|
proc newSelector*(): PSelector =
|
||||||
|
|
@ -116,15 +121,8 @@ when defined(linux) or defined(nimdoc):
|
||||||
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
||||||
## Determines whether selector contains a file descriptor.
|
## Determines whether selector contains a file descriptor.
|
||||||
if s.fds.hasKey(fd):
|
if s.fds.hasKey(fd):
|
||||||
result = true
|
|
||||||
|
|
||||||
# Ensure the underlying epoll instance still contains this fd.
|
# Ensure the underlying epoll instance still contains this fd.
|
||||||
var event = createEventStruct(s.fds[fd].events, fd)
|
result = epollHasFd(s, fd)
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
|
||||||
let err = osLastError()
|
|
||||||
if err.cint in {ENOENT, EBADF}:
|
|
||||||
return false
|
|
||||||
OSError(OSLastError())
|
|
||||||
else:
|
else:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2364,12 +2364,32 @@ when not defined(JS): #and not defined(NimrodVM):
|
||||||
|
|
||||||
when not defined(NimrodVM):
|
when not defined(NimrodVM):
|
||||||
proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
|
proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
|
||||||
## can be used to mark a condition to be likely. This is a hint for the
|
## Hints the optimizer that `val` is likely going to be true.
|
||||||
## optimizer.
|
##
|
||||||
|
## You can use this proc to decorate a branch condition. On certain
|
||||||
|
## platforms this can help the processor predict better which branch is
|
||||||
|
## going to be run. Example:
|
||||||
|
##
|
||||||
|
## .. code-block:: nimrod
|
||||||
|
## for value in inputValues:
|
||||||
|
## if likely(value <= 100):
|
||||||
|
## process(value)
|
||||||
|
## else:
|
||||||
|
## echo "Value too big!"
|
||||||
|
|
||||||
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
|
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
|
||||||
## can be used to mark a condition to be unlikely. This is a hint for the
|
## Hints the optimizer that `val` is likely going to be false.
|
||||||
## optimizer.
|
##
|
||||||
|
## You can use this proc to decorate a branch condition. On certain
|
||||||
|
## platforms this can help the processor predict better which branch is
|
||||||
|
## going to be run. Example:
|
||||||
|
##
|
||||||
|
## .. code-block:: nimrod
|
||||||
|
## for value in inputValues:
|
||||||
|
## if unlikely(value > 100):
|
||||||
|
## echo "Value too big!"
|
||||||
|
## else:
|
||||||
|
## process(value)
|
||||||
|
|
||||||
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
|
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
|
||||||
## retrieves the raw proc pointer of the closure `x`. This is
|
## retrieves the raw proc pointer of the closure `x`. This is
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ when defined(windows):
|
||||||
elif defined(macosx):
|
elif defined(macosx):
|
||||||
const libz = "libz.dylib"
|
const libz = "libz.dylib"
|
||||||
else:
|
else:
|
||||||
const libz = "libz.so"
|
const libz = "libz.so.1"
|
||||||
|
|
||||||
type
|
type
|
||||||
Uint* = int32
|
Uint* = int32
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
msg: "type mismatch: got (array[0..2, float], array[0..1, float])"
|
errmsg: "type mismatch: got (array[0..2, float], array[0..1, float])"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc `+`*[R, T] (v1, v2: array[R, T]): array[R, T] =
|
proc `+`*[R, T] (v1, v2: array[R, T]): array[R, T] =
|
||||||
|
|
|
||||||
|
|
@ -14,31 +14,31 @@ const
|
||||||
|
|
||||||
var clientCount = 0
|
var clientCount = 0
|
||||||
|
|
||||||
proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
|
proc sendMessages(disp: PDispatcher, client: TSocketHandle) {.async.} =
|
||||||
for i in 0 .. <messagesToSend:
|
for i in 0 .. <messagesToSend:
|
||||||
discard await disp.send(client, "Message " & $i & "\c\L")
|
await disp.send(client, "Message " & $i & "\c\L")
|
||||||
|
|
||||||
proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
|
proc launchSwarm(disp: PDispatcher, port: TPort) {.async.} =
|
||||||
for i in 0 .. <swarmSize:
|
for i in 0 .. <swarmSize:
|
||||||
var sock = disp.socket()
|
var sock = disp.socket()
|
||||||
|
|
||||||
#disp.register(sock)
|
#disp.register(sock)
|
||||||
discard await disp.connect(sock, "localhost", port)
|
await disp.connect(sock, "localhost", port)
|
||||||
when true:
|
when true:
|
||||||
discard await sendMessages(disp, sock)
|
await sendMessages(disp, sock)
|
||||||
sock.close()
|
disp.close(sock)
|
||||||
else:
|
else:
|
||||||
# Issue #932: https://github.com/Araq/Nimrod/issues/932
|
# Issue #932: https://github.com/Araq/Nimrod/issues/932
|
||||||
var msgFut = sendMessages(disp, sock)
|
var msgFut = sendMessages(disp, sock)
|
||||||
msgFut.callback =
|
msgFut.callback =
|
||||||
proc () =
|
proc () =
|
||||||
sock.close()
|
disp.close(sock)
|
||||||
|
|
||||||
proc readMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
|
proc readMessages(disp: PDispatcher, client: TSocketHandle) {.async.} =
|
||||||
while true:
|
while true:
|
||||||
var line = await disp.recvLine(client)
|
var line = await disp.recvLine(client)
|
||||||
if line == "":
|
if line == "":
|
||||||
client.close()
|
disp.close(client)
|
||||||
clientCount.inc
|
clientCount.inc
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
|
@ -47,16 +47,18 @@ proc readMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.asyn
|
||||||
else:
|
else:
|
||||||
doAssert false
|
doAssert false
|
||||||
|
|
||||||
proc createServer(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
|
proc createServer(disp: PDispatcher, port: TPort) {.async.} =
|
||||||
var server = disp.socket()
|
var server = disp.socket()
|
||||||
#disp.register(server)
|
#disp.register(server)
|
||||||
server.bindAddr(port)
|
server.bindAddr(port)
|
||||||
server.listen()
|
server.listen()
|
||||||
while true:
|
while true:
|
||||||
discard readMessages(disp, await disp.accept(server))
|
var client = await disp.accept(server)
|
||||||
|
readMessages(disp, client)
|
||||||
|
# TODO: Test: readMessages(disp, await disp.accept(server))
|
||||||
|
|
||||||
discard disp.createServer(TPort(10335))
|
disp.createServer(TPort(10335))
|
||||||
discard disp.launchSwarm(TPort(10335))
|
disp.launchSwarm(TPort(10335))
|
||||||
while true:
|
while true:
|
||||||
disp.poll()
|
disp.poll()
|
||||||
if clientCount == swarmSize: break
|
if clientCount == swarmSize: break
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
line: 11
|
line: 10
|
||||||
file: "tbindtypedesc.nim"
|
|
||||||
errormsg: "type mismatch: got (typedesc[float], string)"
|
errormsg: "type mismatch: got (typedesc[float], string)"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
line: 1855
|
line: 1913
|
||||||
file: "system.nim"
|
file: "system.nim"
|
||||||
errormsg: "can raise an unlisted exception: ref EIO"
|
errormsg: "can raise an unlisted exception: ref EIO"
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
7
tests/generics/tbadgenericlambda.nim
Normal file
7
tests/generics/tbadgenericlambda.nim
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
discard """
|
||||||
|
errmsg: "nested proc can have generic parameters only when"
|
||||||
|
line: 6
|
||||||
|
"""
|
||||||
|
|
||||||
|
let x = proc (x, y): auto = x + y
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
output: "10\n10\n1\n2\n3"
|
output: "10\n10\n1\n2\n3\n15"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc test(x: proc (a, b: int): int) =
|
proc test(x: proc (a, b: int): int) =
|
||||||
|
|
@ -16,3 +16,8 @@ proc foreach[T](s: seq[T], body: proc(x: T)) =
|
||||||
foreach(@[1,2,3]) do (x):
|
foreach(@[1,2,3]) do (x):
|
||||||
echo x
|
echo x
|
||||||
|
|
||||||
|
proc foo =
|
||||||
|
let x = proc (a, b: int): auto = a + b
|
||||||
|
echo x(5, 10)
|
||||||
|
|
||||||
|
foo()
|
||||||
|
|
|
||||||
16
tests/generics/tlateboundstatic.nim
Normal file
16
tests/generics/tlateboundstatic.nim
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
discard """
|
||||||
|
msg: "array[0..3, int]"
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
KK[I: static[int]] = object
|
||||||
|
x: array[I, int]
|
||||||
|
|
||||||
|
proc foo(a: static[string]): KK[a.len] =
|
||||||
|
result.x[0] = 12
|
||||||
|
|
||||||
|
var x = foo "test"
|
||||||
|
|
||||||
|
import typetraits
|
||||||
|
static: echo x.x.type.name
|
||||||
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nimrod check $# $#"
|
cmd: "nimrod check $# $#"
|
||||||
msg: "'proc' is not a concrete type"
|
errmsg: "'proc' is not a concrete type"
|
||||||
msg: "'Foo' is not a concrete type."
|
errmsg: "'Foo' is not a concrete type."
|
||||||
msg: "invalid type: 'TBaseMed'"
|
errmsg: "invalid type: 'TBaseMed'"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
9
tests/generics/tsigtypeop.nim
Normal file
9
tests/generics/tsigtypeop.nim
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
type
|
||||||
|
Vec3[T] = array[3, T]
|
||||||
|
|
||||||
|
proc foo(x: Vec3, y: Vec3.T, z: x.T): x.type.T =
|
||||||
|
return 10
|
||||||
|
|
||||||
|
var y: Vec3[int] = [1, 2, 3]
|
||||||
|
var z: int = foo(y, 3, 4)
|
||||||
|
|
||||||
23
tests/matrix/issue1013.nim
Normal file
23
tests/matrix/issue1013.nim
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import typetraits
|
||||||
|
|
||||||
|
template reject(e: expr) =
|
||||||
|
static: assert(not compiles(e))
|
||||||
|
|
||||||
|
type
|
||||||
|
TMatrix[T; M, N: static[int]] = array[M*N, T]
|
||||||
|
|
||||||
|
proc `*`[T; R, N, C](a: TMatrix[T, R, N], b: TMatrix[T, N, C]): TMatrix[T, R, C] =
|
||||||
|
discard
|
||||||
|
|
||||||
|
var m1: TMatrix[int, 6, 4]
|
||||||
|
var m2: TMatrix[int, 4, 3]
|
||||||
|
var m3: TMatrix[int, 3, 3]
|
||||||
|
|
||||||
|
var m4 = m1*m2
|
||||||
|
static: assert m4.M == 6 and m4.N == 3
|
||||||
|
|
||||||
|
reject m1 * m3 # not compatible
|
||||||
|
|
||||||
|
var m5 = m2 * m3
|
||||||
|
static: assert high(m5) == 11 # 4*3 - 1
|
||||||
|
|
||||||
79
tests/metatype/swizzle.nim
Normal file
79
tests/metatype/swizzle.nim
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
discard """
|
||||||
|
output: '''3
|
||||||
|
[1, 3]
|
||||||
|
[2, 1, 2]
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import macros, strutils
|
||||||
|
|
||||||
|
template accept(e: expr) =
|
||||||
|
static: assert(compiles(e))
|
||||||
|
|
||||||
|
template reject(e: expr) =
|
||||||
|
static: assert(not compiles(e))
|
||||||
|
|
||||||
|
proc swizzleIdx(c: char): int =
|
||||||
|
return case c
|
||||||
|
of 'x': 0
|
||||||
|
of 'y': 1
|
||||||
|
of 'z': 2
|
||||||
|
of 'w': 3
|
||||||
|
of 'r': 0
|
||||||
|
of 'g': 1
|
||||||
|
of 'b': 2
|
||||||
|
of 'a': 3
|
||||||
|
else: 0
|
||||||
|
|
||||||
|
proc isSwizzle(s: string): bool =
|
||||||
|
template trySet(name, set) =
|
||||||
|
block search:
|
||||||
|
for c in s:
|
||||||
|
if c notin set:
|
||||||
|
break search
|
||||||
|
return true
|
||||||
|
|
||||||
|
trySet coords, {'x', 'y', 'z', 'w'}
|
||||||
|
trySet colors, {'r', 'g', 'b', 'a'}
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
type
|
||||||
|
StringIsSwizzle = generic value
|
||||||
|
value.isSwizzle
|
||||||
|
|
||||||
|
SwizzleStr = static[string] and StringIsSwizzle
|
||||||
|
|
||||||
|
proc foo(x: SwizzleStr) =
|
||||||
|
echo "sw"
|
||||||
|
|
||||||
|
accept foo("xx")
|
||||||
|
reject foo("xe")
|
||||||
|
|
||||||
|
type
|
||||||
|
Vec[N: static[int]; T] = array[N, T]
|
||||||
|
|
||||||
|
|
||||||
|
proc card(x: Vec): int = x.N
|
||||||
|
proc `$`(x: Vec): string = x.repr.strip
|
||||||
|
|
||||||
|
macro `.`(x: Vec, swizzle: SwizzleStr): expr =
|
||||||
|
var
|
||||||
|
cardinality = swizzle.len
|
||||||
|
values = newNimNode(nnkBracket)
|
||||||
|
v = genSym()
|
||||||
|
|
||||||
|
for c in swizzle:
|
||||||
|
values.add newNimNode(nnkBracketExpr).add(
|
||||||
|
v, c.swizzleIdx.newIntLitNode)
|
||||||
|
|
||||||
|
return quote do:
|
||||||
|
let `v` = `x`
|
||||||
|
Vec[`cardinality`, `v`.T](`values`)
|
||||||
|
|
||||||
|
var z = Vec([1, 2, 3])
|
||||||
|
|
||||||
|
echo z.card
|
||||||
|
echo z.xz
|
||||||
|
echo z.yxy
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tstaticparams.nim"
|
file: "tstaticparams.nim"
|
||||||
output: "abracadabra\ntest\n3\n15\4"
|
output: "abracadabra\ntest\n3\n15\n4\n2"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -14,6 +14,9 @@ type
|
||||||
TA2[T; I: static[int]] = array[0..I, T]
|
TA2[T; I: static[int]] = array[0..I, T]
|
||||||
TA3[T; I: static[int]] = array[I-1, T]
|
TA3[T; I: static[int]] = array[I-1, T]
|
||||||
|
|
||||||
|
TObj = object
|
||||||
|
x: TA3[int, 3]
|
||||||
|
|
||||||
proc takeFoo(x: TFoo) =
|
proc takeFoo(x: TFoo) =
|
||||||
echo "abracadabra"
|
echo "abracadabra"
|
||||||
echo TFoo.Val
|
echo TFoo.Val
|
||||||
|
|
@ -28,6 +31,7 @@ var
|
||||||
t1: TA1[float, 1]
|
t1: TA1[float, 1]
|
||||||
t2: TA2[string, 4]
|
t2: TA2[string, 4]
|
||||||
t3: TA3[int, 10]
|
t3: TA3[int, 10]
|
||||||
|
t4: TObj
|
||||||
|
|
||||||
# example from the manual:
|
# example from the manual:
|
||||||
type
|
type
|
||||||
|
|
@ -45,3 +49,10 @@ proc getRows(mtx: Matrix): int =
|
||||||
result = mtx.M
|
result = mtx.M
|
||||||
|
|
||||||
echo getRows(m)
|
echo getRows(m)
|
||||||
|
|
||||||
|
# issue 997
|
||||||
|
type TTest[T: static[int], U: static[int]] = array[0..T*U, int]
|
||||||
|
type TTestSub[N: static[int]] = TTest[1, N]
|
||||||
|
|
||||||
|
var z: TTestSub[2]
|
||||||
|
echo z.high
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ foo 10
|
||||||
foo "test"
|
foo "test"
|
||||||
foo(@[TObj(x: 10), TObj(x: 20)])
|
foo(@[TObj(x: 10), TObj(x: 20)])
|
||||||
|
|
||||||
proc intval(x: int) = discard
|
proc intval(x: int): int = 10
|
||||||
|
|
||||||
# check real and virtual fields
|
# check real and virtual fields
|
||||||
type
|
type
|
||||||
|
|
@ -34,6 +34,7 @@ type
|
||||||
T.x
|
T.x
|
||||||
y(T)
|
y(T)
|
||||||
intval T.y
|
intval T.y
|
||||||
|
let z = intval(T.y)
|
||||||
|
|
||||||
proc y(x: TObj): int = 10
|
proc y(x: TObj): int = 10
|
||||||
|
|
||||||
|
|
|
||||||
24
tests/metatype/tusertypeclasses2.nim
Normal file
24
tests/metatype/tusertypeclasses2.nim
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
type
|
||||||
|
hasFieldX = generic z
|
||||||
|
z.x is int
|
||||||
|
|
||||||
|
obj_x = object
|
||||||
|
x: int
|
||||||
|
|
||||||
|
ref_obj_x = ref object
|
||||||
|
x: int
|
||||||
|
|
||||||
|
ref_to_obj_x = ref obj_x
|
||||||
|
|
||||||
|
p_o_x = ptr obj_x
|
||||||
|
v_o_x = var obj_x
|
||||||
|
|
||||||
|
template check(x) =
|
||||||
|
static: assert(x)
|
||||||
|
|
||||||
|
check obj_x is hasFieldX
|
||||||
|
check ref_obj_x is hasFieldX
|
||||||
|
check ref_to_obj_x is hasFieldX
|
||||||
|
check p_o_x is hasFieldX
|
||||||
|
check v_o_x is hasFieldX
|
||||||
|
|
||||||
10
tests/metatype/typeclassinference.nim
Normal file
10
tests/metatype/typeclassinference.nim
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import typetraits
|
||||||
|
|
||||||
|
type
|
||||||
|
Vec[N: static[int]; T] = distinct array[N, T]
|
||||||
|
|
||||||
|
var x = Vec([1, 2, 3])
|
||||||
|
|
||||||
|
static:
|
||||||
|
assert x.type.name == "Vec[static[int](3), int]"
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ type
|
||||||
C.len is Ordinal
|
C.len is Ordinal
|
||||||
items(c) is iterator
|
items(c) is iterator
|
||||||
for value in C:
|
for value in C:
|
||||||
value.type is T
|
type(value) is T
|
||||||
|
|
||||||
proc takesIntContainer(c: Container[int]) =
|
proc takesIntContainer(c: Container[int]) =
|
||||||
for e in c: echo e
|
for e in c: echo e
|
||||||
|
|
|
||||||
49
tests/objects/tobjpragma.nim
Normal file
49
tests/objects/tobjpragma.nim
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
discard """
|
||||||
|
file: "tobjpragma.nim"
|
||||||
|
output: '''2
|
||||||
|
3
|
||||||
|
9
|
||||||
|
257
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Test
|
||||||
|
|
||||||
|
type
|
||||||
|
Foo {.packed.} = object
|
||||||
|
a: int8
|
||||||
|
b: int8
|
||||||
|
|
||||||
|
Bar {.packed.} = object
|
||||||
|
a: int8
|
||||||
|
b: int16
|
||||||
|
|
||||||
|
Daz {.packed.} = object
|
||||||
|
a: int32
|
||||||
|
b: int8
|
||||||
|
c: int32
|
||||||
|
|
||||||
|
|
||||||
|
var f = Foo(a: 1, b: 1)
|
||||||
|
var b: Bar
|
||||||
|
var d: Daz
|
||||||
|
|
||||||
|
echo sizeof(f)
|
||||||
|
echo sizeof(b)
|
||||||
|
echo sizeof(d)
|
||||||
|
echo (cast[ptr int16](f.addr)[])
|
||||||
|
|
||||||
|
type
|
||||||
|
Union {.union.} = object
|
||||||
|
a: int8
|
||||||
|
b: int8
|
||||||
|
|
||||||
|
var u: Union
|
||||||
|
u.a = 1
|
||||||
|
echo u.b
|
||||||
|
u.a = 2
|
||||||
|
echo u.b
|
||||||
|
u.b = 3
|
||||||
|
echo u.a
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
msg: 'type mismatch: got (PTest)'
|
errormsg: "type mismatch: got (PTest)"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ type
|
||||||
rule: TNode ## the rule that the symbol refers to
|
rule: TNode ## the rule that the symbol refers to
|
||||||
TNode {.final, shallow.} = object
|
TNode {.final, shallow.} = object
|
||||||
case kind: TPegKind
|
case kind: TPegKind
|
||||||
of pkEmpty..pkWhitespace: discard
|
of pkEmpty..pkWhitespace: nil
|
||||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
|
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
|
||||||
of pkChar, pkGreedyRepChar: ch: char
|
of pkChar, pkGreedyRepChar: ch: char
|
||||||
of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
|
of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,11 @@ proc parseSpec*(filename: string): TSpec =
|
||||||
result.substr = true
|
result.substr = true
|
||||||
of "exitcode":
|
of "exitcode":
|
||||||
discard parseInt(e.value, result.exitCode)
|
discard parseInt(e.value, result.exitCode)
|
||||||
of "errormsg", "msg":
|
of "msg":
|
||||||
|
result.msg = e.value
|
||||||
|
if result.action != actionRun:
|
||||||
|
result.action = actionCompile
|
||||||
|
of "errormsg":
|
||||||
result.msg = e.value
|
result.msg = e.value
|
||||||
result.action = actionReject
|
result.action = actionReject
|
||||||
of "disabled":
|
of "disabled":
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
parseutils, strutils, pegs, os, osproc, streams, parsecfg, json,
|
parseutils, strutils, pegs, os, osproc, streams, parsecfg, json,
|
||||||
marshal, backend, parseopt, specs, htmlgen, browsers
|
marshal, backend, parseopt, specs, htmlgen, browsers, terminal
|
||||||
|
|
||||||
const
|
const
|
||||||
resultsFile = "testresults.html"
|
resultsFile = "testresults.html"
|
||||||
|
|
@ -109,6 +109,12 @@ proc addResult(r: var TResults, test: TTest,
|
||||||
expected = expected,
|
expected = expected,
|
||||||
given = given)
|
given = given)
|
||||||
r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success)
|
r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success)
|
||||||
|
if success notin {reSuccess, reIgnored}:
|
||||||
|
styledEcho styleBright, name, fgRed, " [", $success, "]"
|
||||||
|
styledEcho styleDim, "EXPECTED:"
|
||||||
|
echo expected
|
||||||
|
styledEcho styleDim, "GIVEN:"
|
||||||
|
echo given
|
||||||
|
|
||||||
proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) =
|
proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) =
|
||||||
if strip(expected.msg) notin strip(given.msg):
|
if strip(expected.msg) notin strip(given.msg):
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
discard """
|
discard """
|
||||||
output: "Error: cannot evaluate at compile time: x"
|
errormsg: "cannot evaluate at compile time: x"
|
||||||
line: 10
|
line: 9
|
||||||
"""
|
"""
|
||||||
|
|
||||||
var x: array[100, char]
|
var x: array[100, char]
|
||||||
template Foo : expr = x[42]
|
template Foo : expr = x[42]
|
||||||
|
|
||||||
|
|
||||||
const myConst = foo
|
const myConst = foo
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
output: "Error: cannot evaluate at compile time: x"
|
errormsg: "cannot evaluate at compile time: x"
|
||||||
line: 7
|
line: 7
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,8 @@ News
|
||||||
evaluation.
|
evaluation.
|
||||||
- ``--gc:none`` produces warnings when code uses the GC.
|
- ``--gc:none`` produces warnings when code uses the GC.
|
||||||
- A ``union`` pragma for better C interoperability is now supported.
|
- A ``union`` pragma for better C interoperability is now supported.
|
||||||
|
- A ``packed`` pragma to control the memory packing/alignment of fields in
|
||||||
|
an object.
|
||||||
- Arrays can be annotated to be ``unchecked`` for easier low level
|
- Arrays can be annotated to be ``unchecked`` for easier low level
|
||||||
manipulations of memory.
|
manipulations of memory.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue