Merge branch 'devel' of https://github.com/Araq/Nimrod into devel

This commit is contained in:
Araq 2014-03-22 02:52:33 +01:00
commit 5aac789e0e
53 changed files with 1264 additions and 329 deletions

View file

@ -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

View file

@ -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", [])

View file

@ -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,

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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) =

View file

@ -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]",

View file

@ -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)

View file

@ -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:

View file

@ -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:

View file

@ -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 =

View file

@ -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

View file

@ -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())

View file

@ -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) =

View file

@ -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()

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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})

View file

@ -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)

View file

@ -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

View file

@ -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",

View file

@ -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
---------------- ----------------

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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] =

View file

@ -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

View file

@ -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)"
""" """

View file

@ -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"
""" """

View 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

View file

@ -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()

View 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

View file

@ -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

View 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)

View 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

View 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

View file

@ -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

View file

@ -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

View 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

View 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]"

View file

@ -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

View 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

View file

@ -1,5 +1,5 @@
discard """ discard """
msg: 'type mismatch: got (PTest)' errormsg: "type mismatch: got (PTest)"
""" """
type type

View file

@ -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]

View file

@ -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":

View file

@ -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):

View file

@ -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

View file

@ -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
""" """

View file

@ -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.