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

This commit is contained in:
Araq 2014-01-13 01:22:03 +01:00
commit 51ee524109
55 changed files with 1559 additions and 931 deletions

View file

@ -192,6 +192,7 @@ type
nkObjectTy, # object body nkObjectTy, # object body
nkTupleTy, # tuple body nkTupleTy, # tuple body
nkTypeClassTy, # user-defined type class nkTypeClassTy, # user-defined type class
nkStaticTy, # ``static[T]``
nkRecList, # list of object parts nkRecList, # list of object parts
nkRecCase, # case section of object nkRecCase, # case section of object
nkRecWhen, # when section of object nkRecWhen, # when section of object
@ -336,18 +337,37 @@ type
tyIter, # unused tyIter, # unused
tyProxy # used as errornous type (for idetools) tyProxy # used as errornous type (for idetools)
tyTypeClass tyTypeClass
tyAnd
tyOr
tyNot
tyAnything
tyParametricTypeClass # structured similarly to tyGenericInst tyParametricTypeClass # structured similarly to tyGenericInst
# lastSon is the body of the type class # lastSon is the body of the type class
tyBuiltInTypeClass # Type such as the catch-all object, tuple, seq, etc
tyCompositeTypeClass #
tyAnd, tyOr, tyNot # boolean type classes such as `string|int`,`not seq`,
# `Sortable and Enumable`, etc
tyAnything # a type class matching any type
tyStatic # a value known at compile type (the underlying type is .base)
tyFromExpr # This is a type representing an expression that depends
# on generic parameters (the exprsesion is stored in t.n)
# It will be converted to a real type only during generic
# instantiation and prior to this it has the potential to
# be any type.
const const
tyPureObject* = tyTuple tyPureObject* = tyTuple
GcTypeKinds* = {tyRef, tySequence, tyString} GcTypeKinds* = {tyRef, tySequence, tyString}
tyError* = tyProxy # as an errornous node should match everything tyError* = tyProxy # as an errornous node should match everything
tyTypeClasses* = {tyTypeClass, tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything}
tyUnknownTypes* = {tyError, tyFromExpr}
tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyCompositeTypeClass,
tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything}
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses
type type
TTypeKinds* = set[TTypeKind] TTypeKinds* = set[TTypeKind]
@ -383,9 +403,6 @@ type
# proc foo(T: typedesc, list: seq[T]): var T # proc foo(T: typedesc, list: seq[T]): var T
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)
tfAll, # type class requires all constraints to be met (default)
tfAny, # type class requires any constraint to be met
tfNot, # type class with a negative check
tfCapturesEnv, # whether proc really captures some environment tfCapturesEnv, # whether proc really captures some environment
tfByCopy, # pass object/tuple by copy (C backend) tfByCopy, # pass object/tuple by copy (C backend)
tfByRef, # pass object/tuple by reference (C backend) tfByRef, # pass object/tuple by reference (C backend)
@ -396,8 +413,12 @@ type
tfNeedsInit, # type constains a "not nil" constraint somewhere or some tfNeedsInit, # type constains a "not nil" constraint somewhere or some
# other type so that it requires inititalization # other type so that it requires inititalization
tfHasShared, # type constains a "shared" constraint modifier somewhere tfHasShared, # type constains a "shared" constraint modifier somewhere
tfHasMeta, # type has "typedesc" or "expr" somewhere; or uses '|' tfHasMeta, # type contains "wildcard" sub-types such as generic params
# or other type classes
tfHasGCedMem, # type contains GC'ed memory tfHasGCedMem, # type contains GC'ed memory
tfHasStatic
tfGenericTypeParam
tfImplicitTypeParam
TTypeFlags* = set[TTypeFlag] TTypeFlags* = set[TTypeFlag]
@ -774,9 +795,11 @@ const
GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody, GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody,
tyGenericParam} tyGenericParam}
StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray, StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray,
tySet, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tyOpenArray, tySet, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tyOpenArray,
tyVarargs} tyVarargs}
ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in:: ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in::
# var x = expr # var x = expr
tyBool, tyChar, tyEnum, tyArray, tyObject, tyBool, tyChar, tyEnum, tyArray, tyObject,
@ -895,6 +918,9 @@ template `{}=`*(n: PNode, i: int, s: PNode): stmt =
var emptyNode* = newNode(nkEmpty) 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 =
return t.kind in tyMetaTypes 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
s.typ = t s.typ = t
@ -1216,10 +1242,14 @@ proc newSons(father: PNode, length: int) =
else: else:
setLen(father.sons, length) setLen(father.sons, length)
proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
result = t
while result.kind in kinds: result = lastSon(result)
proc propagateToOwner*(owner, elem: PType) = proc propagateToOwner*(owner, elem: PType) =
const HaveTheirOwnEmpty = {tySequence, tySet} const HaveTheirOwnEmpty = {tySequence, tySet}
owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta, owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta,
tfHasGCedMem}) tfHasStatic, tfHasGCedMem})
if tfNotNil in elem.flags: if tfNotNil in elem.flags:
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}: if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}:
owner.flags.incl tfNotNil owner.flags.incl tfNotNil
@ -1233,9 +1263,13 @@ 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 {tyExpr, tyTypeDesc}: if elem.kind in tyMetaTypes:
owner.flags.incl tfHasMeta owner.flags.incl tfHasMeta
elif elem.kind in {tyString, tyRef, tySequence} or
if elem.kind == tyStatic:
owner.flags.incl tfHasStatic
if elem.kind in {tyString, tyRef, tySequence} or
elem.kind == tyProc and elem.callConv == ccClosure: elem.kind == tyProc and elem.callConv == ccClosure:
owner.flags.incl tfHasGCedMem owner.flags.incl tfHasGCedMem
@ -1409,6 +1443,10 @@ proc skipGenericOwner*(s: PSym): PSym =
result = if sfFromGeneric in s.flags: s.owner.owner result = if sfFromGeneric in s.flags: s.owner.owner
else: s.owner else: s.owner
proc originatingModule*(s: PSym): PSym =
result = s.owner
while result.kind != skModule: result = result.owner
proc isRoutine*(s: PSym): bool {.inline.} = proc isRoutine*(s: PSym): bool {.inline.} =
result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod, result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod,
skConverter} skConverter}

View file

@ -432,6 +432,8 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope =
proc debug(n: PSym) = proc debug(n: PSym) =
if n == nil: if n == nil:
writeln(stdout, "null") writeln(stdout, "null")
elif n.kind == skUnknown:
writeln(stdout, "skUnknown")
else: else:
#writeln(stdout, ropeToStr(symToYaml(n, 0, 1))) #writeln(stdout, ropeToStr(symToYaml(n, 0, 1)))
writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4", [ writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4", [

View file

@ -86,9 +86,8 @@ proc getUniqueType*(key: PType): PType =
if result == nil: if result == nil:
gCanonicalTypes[k] = key gCanonicalTypes[k] = key
result = key result = key
of tyTypeDesc, tyTypeClasses: of tyTypeDesc, tyTypeClasses, tyGenericParam,
internalError("value expected, but got a type") tyFromExpr, tyStatic:
of tyGenericParam:
internalError("GetUniqueType") internalError("GetUniqueType")
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter: of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter:
result = getUniqueType(lastSon(key)) result = getUniqueType(lastSon(key))

View file

@ -194,7 +194,7 @@ when compileTimeRopeFmt:
if i - 1 >= start: if i - 1 >= start:
yield (kind: ffLit, value: substr(s, start, i-1), intValue: 0) yield (kind: ffLit, value: substr(s, start, i-1), intValue: 0)
macro rfmt(m: BModule, fmt: expr[string], args: varargs[PRope]): expr = macro rfmt(m: BModule, fmt: static[string], args: varargs[PRope]): expr =
## Experimental optimized rope-formatting operator ## Experimental optimized rope-formatting operator
## The run-time code it produces will be very fast, but will it speed up ## The run-time code it produces will be very fast, but will it speed up
## the compilation of nimrod itself or will the macro execution time ## the compilation of nimrod itself or will the macro execution time
@ -209,7 +209,7 @@ when compileTimeRopeFmt:
of ffParam: of ffParam:
result.add(args[frag.intValue]) result.add(args[frag.intValue])
else: else:
template rfmt(m: BModule, fmt: expr[string], args: varargs[PRope]): expr = template rfmt(m: BModule, fmt: string, args: varargs[PRope]): expr =
ropecg(m, fmt, args) ropecg(m, fmt, args)
proc appcg(m: BModule, c: var PRope, frmt: TFormatStr, proc appcg(m: BModule, c: var PRope, frmt: TFormatStr,
@ -945,42 +945,58 @@ proc genFilenames(m: BModule): PRope =
proc genMainProc(m: BModule) = proc genMainProc(m: BModule) =
const const
CommonMainBody = PreMainBody =
"\tsystemDatInit();$n" & "\tsystemDatInit();$n" &
"\tsystemInit();$n" & "\tsystemInit();$n" &
"$1" & "$1" &
"$2" & "$2" &
"$3" & "$3" &
"$4" "$4"
MainProcs =
"\tPreMain();$n" &
"\tNimMain();$n"
MainProcsWithResult =
MainProcs & "\treturn nim_program_result;$n"
PosixNimMain = PosixNimMain =
"int cmdCount;$n" & "int cmdCount;$n" &
"char** cmdLine;$n" & "char** cmdLine;$n" &
"char** gEnv;$n" & "char** gEnv;$n" &
"N_CDECL(void, NimMain)(void) {$n" & "N_CDECL(void, NimMain)(void) {$n$1}$n"
CommonMainBody & "}$n"
PosixCMain = "int main(int argc, char** args, char** env) {$n" & PosixCMain = "int main(int argc, char** args, char** env) {$n" &
"\tcmdLine = args;$n" & "\tcmdCount = argc;$n" & "\tgEnv = env;$n" & "\tcmdLine = args;$n" & "\tcmdCount = argc;$n" & "\tgEnv = env;$n" &
"\tNimMain();$n" & "\treturn nim_program_result;$n" & "}$n" MainProcsWithResult &
"}$n"
StandaloneCMain = "int main(void) {$n" & StandaloneCMain = "int main(void) {$n" &
"\tNimMain();$n" & MainProcs &
"\treturn 0;$n" & "}$n" "\treturn 0;$n" & "}$n"
WinNimMain = "N_CDECL(void, NimMain)(void) {$n" &
CommonMainBody & "}$n" WinNimMain = "N_CDECL(void, NimMain)(void) {$n$1}$n"
WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $n" & WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $n" &
" HINSTANCE hPrevInstance, $n" & " HINSTANCE hPrevInstance, $n" &
" LPSTR lpCmdLine, int nCmdShow) {$n" & " LPSTR lpCmdLine, int nCmdShow) {$n" &
"\tNimMain();$n" & "\treturn nim_program_result;$n" & "}$n" MainProcsWithResult & "}$n"
WinNimDllMain = "N_LIB_EXPORT N_CDECL(void, NimMain)(void) {$n" &
CommonMainBody & "}$n" WinNimDllMain = "N_LIB_EXPORT N_CDECL(void, NimMain)(void) {$n$1}$n"
WinCDllMain = WinCDllMain =
"BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n" & "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n" &
" LPVOID lpvReserved) {$n" & " LPVOID lpvReserved) {$n" &
"\tif(fwdreason == DLL_PROCESS_ATTACH) NimMain();$n" & "\tif(fwdreason == DLL_PROCESS_ATTACH) {" & MainProcs & "}$n" &
"\treturn 1;$n" & "}$n" "\treturn 1;$n}$n"
PosixNimDllMain = WinNimDllMain PosixNimDllMain = WinNimDllMain
PosixCDllMain = PosixCDllMain =
"void NIM_POSIX_INIT NimMainInit(void) {$n" & "void NIM_POSIX_INIT NimMainInit(void) {$n" &
"\tNimMain();$n}$n" MainProcs &
"}$n"
var nimMain, otherMain: TFormatStr var nimMain, otherMain: TFormatStr
if platform.targetOS == osWindows and if platform.targetOS == osWindows and
gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}: gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}:
@ -1008,8 +1024,10 @@ proc genMainProc(m: BModule) =
platform.targetOS == osStandalone: "".toRope platform.targetOS == osStandalone: "".toRope
else: ropecg(m, "\t#initStackBottom();$n") else: ropecg(m, "\t#initStackBottom();$n")
inc(m.labels) inc(m.labels)
appcg(m, m.s[cfsProcs], nimMain, [mainDatInit, initStackBottomCall, appcg(m, m.s[cfsProcs], "void PreMain() {$n" & PreMainBody & "}$n", [
gBreakpoints, mainModInit, toRope(m.labels)]) mainDatInit, initStackBottomCall, gBreakpoints, otherModsInit])
appcg(m, m.s[cfsProcs], nimMain, [mainModInit, toRope(m.labels)])
if optNoMain notin gGlobalOptions: if optNoMain notin gGlobalOptions:
appcg(m, m.s[cfsProcs], otherMain, []) appcg(m, m.s[cfsProcs], otherMain, [])
@ -1030,9 +1048,13 @@ proc registerModuleToMain(m: PSym) =
"declare void $1() noinline$N", [init]) "declare void $1() noinline$N", [init])
appff(mainModProcs, "N_NOINLINE(void, $1)(void);$N", appff(mainModProcs, "N_NOINLINE(void, $1)(void);$N",
"declare void $1() noinline$N", [datInit]) "declare void $1() noinline$N", [datInit])
if not (sfSystemModule in m.flags): if sfSystemModule notin m.flags:
appff(mainModInit, "\t$1();$n", "call void ()* $1$n", [init])
appff(mainDatInit, "\t$1();$n", "call void ()* $1$n", [datInit]) appff(mainDatInit, "\t$1();$n", "call void ()* $1$n", [datInit])
let initCall = ropeff("\t$1();$n", "call void ()* $1$n", [init])
if sfMainModule in m.flags:
app(mainModInit, initCall)
else:
app(otherModsInit, initCall)
proc genInitCode(m: BModule) = proc genInitCode(m: BModule) =
var initname = getInitName(m.module) var initname = getInitName(m.module)

View file

@ -114,7 +114,8 @@ type
injectStmt*: PRope injectStmt*: PRope
var var
mainModProcs*, mainModInit*, mainDatInit*: PRope # parts of the main module mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: PRope
# varuious parts of the main module
gMapping*: PRope # the generated mapping file (if requested) gMapping*: PRope # the generated mapping file (if requested)
gModules*: seq[BModule] = @[] # list of all compiled modules gModules*: seq[BModule] = @[] # list of all compiled modules
gForwardedProcsCounter*: int = 0 gForwardedProcsCounter*: int = 0

View file

@ -87,7 +87,7 @@ proc mapType(t: ast.PType): ptr libffi.TType =
of tyFloat, tyFloat64: result = addr libffi.type_double of tyFloat, tyFloat64: result = addr libffi.type_double
of tyFloat32: result = addr libffi.type_float of tyFloat32: result = addr libffi.type_float
of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr, tyNil: tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr, tyStatic, tyNil:
result = addr libffi.type_pointer result = addr libffi.type_pointer
of tyDistinct: of tyDistinct:
result = mapType(t.sons[0]) result = mapType(t.sons[0])

View file

@ -91,6 +91,7 @@ proc evalMacroCall*(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode
proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode
proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode = proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode =
if defined(debug) and gVerbosity >= 3: writeStackTrace()
result = newNodeI(nkExceptBranch, info) result = newNodeI(nkExceptBranch, info)
# creating a nkExceptBranch without sons # creating a nkExceptBranch without sons
# means that it could not be evaluated # means that it could not be evaluated
@ -264,7 +265,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
of tyFloat..tyFloat128: of tyFloat..tyFloat128:
result = newNodeIt(nkFloatLit, info, t) result = newNodeIt(nkFloatLit, info, t)
of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
tyStmt, tyTypeDesc, tyProc: tyStmt, tyTypeDesc, tyStatic, tyProc:
result = newNodeIT(nkNilLit, info, t) result = newNodeIT(nkNilLit, info, t)
of tyObject: of tyObject:
result = newNodeIT(nkPar, info, t) result = newNodeIT(nkPar, info, t)
@ -358,7 +359,7 @@ proc evalVar(c: PEvalContext, n: PNode): PNode =
proc aliasNeeded(n: PNode, flags: TEvalFlags): bool = proc aliasNeeded(n: PNode, flags: TEvalFlags): bool =
result = efLValue in flags or n.typ == nil or result = efLValue in flags or n.typ == nil or
n.typ.kind in {tyExpr, tyStmt, tyTypeDesc} n.typ.kind in {tyExpr, tyStatic, tyStmt, tyTypeDesc}
proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode = proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode =
# We need to return a node to the actual value, # We need to return a node to the actual value,
@ -907,9 +908,7 @@ proc evalParseStmt(c: PEvalContext, n: PNode): PNode =
#result.typ = newType(tyStmt, c.module) #result.typ = newType(tyStmt, c.module)
proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode = proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode =
InternalAssert operand.kind == nkSym let typ = operand.typ.skipTypes({tyTypeDesc})
let typ = operand.sym.typ.skipTypes({tyTypeDesc})
case trait.sym.name.s.normalize case trait.sym.name.s.normalize
of "name": of "name":
result = newStrNode(nkStrLit, typ.typeToString(preferName)) result = newStrNode(nkStrLit, typ.typeToString(preferName))
@ -1329,7 +1328,7 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
if gNestedEvals <= 0: stackTrace(c, n.info, errTooManyIterations) if gNestedEvals <= 0: stackTrace(c, n.info, errTooManyIterations)
case n.kind case n.kind
of nkSym: result = evalSym(c, n, flags) of nkSym: result = evalSym(c, n, flags)
of nkType..nkNilLit: of nkType..nkNilLit, nkTypeOfExpr:
# nkStrLit is VERY common in the traces, so we should avoid # nkStrLit is VERY common in the traces, so we should avoid
# the 'copyNode' here. # the 'copyNode' here.
result = n #.copyNode result = n #.copyNode

View file

@ -129,8 +129,9 @@ proc mapType(typ: PType): TJSTypeKind =
tyVarargs: tyVarargs:
result = etyObject result = etyObject
of tyNil: result = etyNull of tyNil: result = etyNull
of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation, tyNone, of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation,
tyForward, tyEmpty, tyExpr, tyStmt, tyTypeDesc, tyTypeClasses: tyNone, tyFromExpr, tyForward, tyEmpty,
tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses:
result = etyNone result = etyNone
of tyProc: result = etyProc of tyProc: result = etyProc
of tyCString: result = etyString of tyCString: result = etyString

View file

@ -93,6 +93,7 @@ type
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
errOnlyACallOpCanBeDelegator, errUsingNoSymbol, errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
errDestructorNotGenericEnough,
errXExpectsTwoArguments, errXExpectsTwoArguments,
errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
@ -322,6 +323,8 @@ const
errInstantiateXExplicitely: "instantiate '$1' explicitely", errInstantiateXExplicitely: "instantiate '$1' explicitely",
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
"A destructor must be associated will all instantiations of a generic type",
errXExpectsTwoArguments: "\'$1\' expects two arguments", errXExpectsTwoArguments: "\'$1\' expects two arguments",
errXExpectsObjectTypes: "\'$1\' expects object types", errXExpectsObjectTypes: "\'$1\' expects object types",
errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype", errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype",
@ -643,6 +646,8 @@ proc toFileLine*(info: TLineInfo): string {.inline.} =
proc toFileLineCol*(info: TLineInfo): string {.inline.} = proc toFileLineCol*(info: TLineInfo): string {.inline.} =
result = info.toFilename & "(" & $info.line & "," & $info.col & ")" result = info.toFilename & "(" & $info.line & "," & $info.col & ")"
template `$`*(info: TLineInfo): expr = toFileLineCol(info)
proc `??`* (info: TLineInfo, filename: string): bool = proc `??`* (info: TLineInfo, filename: string): bool =
# only for debugging purposes # only for debugging purposes
result = filename in info.toFilename result = filename in info.toFilename
@ -699,21 +704,20 @@ type
TErrorHandling = enum doNothing, doAbort, doRaise TErrorHandling = enum doNothing, doAbort, doRaise
proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
template maybeTrace = template quit =
if defined(debug) or gVerbosity >= 3 or msg == errInternal: if defined(debug) or gVerbosity >= 3 or msg == errInternal:
writeStackTrace() writeStackTrace()
quit 1
if msg >= fatalMin and msg <= fatalMax: if msg >= fatalMin and msg <= fatalMax:
maybeTrace() quit()
quit(1)
if msg >= errMin and msg <= errMax: if msg >= errMin and msg <= errMax:
maybeTrace()
inc(gErrorCounter) inc(gErrorCounter)
options.gExitcode = 1'i8 options.gExitcode = 1'i8
if gErrorCounter >= gErrorMax: if gErrorCounter >= gErrorMax:
quit(1) quit()
elif eh == doAbort and gCmd != cmdIdeTools: elif eh == doAbort and gCmd != cmdIdeTools:
quit(1) quit()
elif eh == doRaise: elif eh == doRaise:
raiseRecoverableError(s) raiseRecoverableError(s)

View file

@ -158,8 +158,6 @@ var
const oKeepVariableNames* = true const oKeepVariableNames* = true
const oUseLateInstantiation* = false
proc mainCommandArg*: string = proc mainCommandArg*: string =
## This is intended for commands like check or parse ## This is intended for commands like check or parse
## which will work on the main project file unless ## which will work on the main project file unless

View file

@ -965,6 +965,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
of tkTuple: result = parseTuple(p, mode == pmTypeDef) of tkTuple: result = parseTuple(p, mode == pmTypeDef)
of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
of tkIterator: of tkIterator:
when true:
if mode in {pmTypeDesc, pmTypeDef}: if mode in {pmTypeDesc, pmTypeDef}:
result = parseProcExpr(p, false) result = parseProcExpr(p, false)
result.kind = nkIteratorTy result.kind = nkIteratorTy
@ -973,6 +974,9 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
parMessage(p, errExprExpected, p.tok) parMessage(p, errExprExpected, p.tok)
getTok(p) # we must consume a token here to prevend endless loops! getTok(p) # we must consume a token here to prevend endless loops!
result = ast.emptyNode result = ast.emptyNode
else:
result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
result.kind = nkIteratorTy
of tkEnum: of tkEnum:
if mode == pmTypeDef: if mode == pmTypeDef:
result = parseEnum(p) result = parseEnum(p)
@ -995,9 +999,13 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
getTokNoInd(p) getTokNoInd(p)
addSon(result, primary(p, pmNormal)) addSon(result, primary(p, pmNormal))
of tkStatic: of tkStatic:
result = newNodeP(nkStaticExpr, p) let info = parLineInfo(p)
getTokNoInd(p) getTokNoInd(p)
addSon(result, primary(p, pmNormal)) let next = primary(p, pmNormal)
if next.kind == nkBracket and next.sonsLen == 1:
result = newNode(nkStaticTy, info, @[next.sons[0]])
else:
result = newNode(nkStaticExpr, info, @[next])
of tkBind: of tkBind:
result = newNodeP(nkBind, p) result = newNodeP(nkBind, p)
getTok(p) getTok(p)

View file

@ -25,12 +25,12 @@ proc equalGenericParams(procA, procB: PNode): bool =
let a = procA.sons[i].sym let a = procA.sons[i].sym
let b = procB.sons[i].sym let b = procB.sons[i].sym
if a.name.id != b.name.id or if a.name.id != b.name.id or
not sameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}): return not sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}): return
if a.ast != nil and b.ast != nil: if a.ast != nil and b.ast != nil:
if not exprStructuralEquivalent(a.ast, b.ast): return if not exprStructuralEquivalent(a.ast, b.ast): return
result = true result = true
proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym =
# Searchs for a forward declaration or a "twin" symbol of fn # Searchs for a forward declaration or a "twin" symbol of fn
# in the symbol table. If the parameter lists are exactly # in the symbol table. If the parameter lists are exactly
# the same the sym in the symbol table is returned, else nil. # the same the sym in the symbol table is returned, else nil.
@ -63,6 +63,30 @@ proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
nil nil
result = nextIdentIter(it, scope.symbols) result = nextIdentIter(it, scope.symbols)
proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
const flags = {ExactGenericParams, ExactTypeDescValues,
ExactConstraints, IgnoreCC}
var it: TIdentIter
result = initIdentIter(it, scope.symbols, fn.name)
while result != nil:
if result.kind in skProcKinds and
sameType(result.typ, fn.typ, flags): return
result = nextIdentIter(it, scope.symbols)
return nil
proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
result = searchForProcNew(c, scope, fn)
when false:
let old = searchForProcOld(c, scope, fn)
if old != result:
echo "Mismatch in searchForProc: ", fn.info
debug fn.typ
debug if result != nil: result.typ else: nil
debug if old != nil: old.typ else: nil
when false: when false:
proc paramsFitBorrow(child, parent: PNode): bool = proc paramsFitBorrow(child, parent: PNode): bool =
var length = sonsLen(child) var length = sonsLen(child)

View file

@ -282,13 +282,16 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope =
app(result, substr(frmt, start, i - 1)) app(result, substr(frmt, start, i - 1))
assert(ropeInvariant(result)) assert(ropeInvariant(result))
{.push stack_trace: off, line_trace: off.} when true:
proc `~`*(r: expr[string]): PRope = template `~`*(r: string): PRope = r.ropef
else:
{.push stack_trace: off, line_trace: off.}
proc `~`*(r: static[string]): PRope =
# this is the new optimized "to rope" operator # this is the new optimized "to rope" operator
# the mnemonic is that `~` looks a bit like a rope :) # the mnemonic is that `~` looks a bit like a rope :)
var r {.global.} = r.ropef var r {.global.} = r.ropef
return r return r
{.pop.} {.pop.}
proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) = proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) =
app(c, ropef(frmt, args)) app(c, ropef(frmt, args))

View file

@ -158,6 +158,16 @@ proc isOpImpl(c: PContext, n: PNode): PNode
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
semCheck: bool = true): PNode semCheck: bool = true): PNode
proc symFromType(t: PType, info: TLineInfo): PSym =
if t.sym != nil: return t.sym
result = newSym(skType, getIdent"AnonType", t.owner, info)
result.flags.incl sfAnon
result.typ = t
proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
result = newSymNode(symFromType(t, info), info)
result.typ = makeTypeDesc(c, t)
when false: when false:
proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext = proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext =
result = newEvalContext(c.module, mode) result = newEvalContext(c.module, mode)
@ -171,7 +181,37 @@ when false:
result.typ = makeTypeDesc(c, result.typ) result.typ = makeTypeDesc(c, result.typ)
result.handleIsOperator = proc (n: PNode): PNode = result.handleIsOperator = proc (n: PNode): PNode =
result = IsOpImpl(c, n) result = isOpImpl(c, n)
proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
# recompute the types as 'eval' isn't guaranteed to construct types nor
# that the types are sound:
result = semExprWithType(c, evaluated)
#result = fitNode(c, e.typ, result) inlined with special case:
let arg = result
result = indexTypesMatch(c, eOrig.typ, arg.typ, arg)
if result == nil:
result = arg
# for 'tcnstseq' we support [] to become 'seq'
if eOrig.typ.skipTypes(abstractInst).kind == tySequence and
arg.typ.skipTypes(abstractInst).kind == tyArrayConstr:
arg.typ = eOrig.typ
proc tryConstExpr(c: PContext, n: PNode): PNode =
var e = semExprWithType(c, n)
if e == nil: return
result = getConstExpr(c.module, e)
if result != nil: return
try:
result = evalConstExpr(c.module, e)
if result == nil or result.kind == nkEmpty:
return nil
result = fixupTypeAfterEval(c, result, e)
except:
return nil
proc semConstExpr(c: PContext, n: PNode): PNode = proc semConstExpr(c: PContext, n: PNode): PNode =
var e = semExprWithType(c, n) var e = semExprWithType(c, n)
@ -191,18 +231,7 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
# error correction: # error correction:
result = e result = e
else: else:
# recompute the types as 'eval' isn't guaranteed to construct types nor result = fixupTypeAfterEval(c, result, e)
# that the types are sound:
result = semExprWithType(c, result)
#result = fitNode(c, e.typ, result) inlined with special case:
let arg = result
result = indexTypesMatch(c, e.typ, arg.typ, arg)
if result == nil:
result = arg
# for 'tcnstseq' we support [] to become 'seq'
if e.typ.skipTypes(abstractInst).kind == tySequence and
arg.typ.skipTypes(abstractInst).kind == tyArrayConstr:
arg.typ = e.typ
include hlo, seminst, semcall include hlo, seminst, semcall
@ -264,6 +293,14 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode =
localError(n.info, errConstExprExpected) localError(n.info, errConstExprExpected)
result = nn result = nn
type
TSemGenericFlag = enum
withinBind, withinTypeDesc, withinMixin
TSemGenericFlags = set[TSemGenericFlag]
proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var TIntSet): PNode
include semtypes, semtempl, semgnrc, semstmts, semexprs include semtypes, semtempl, semgnrc, semstmts, semexprs
proc addCodeForGenerics(c: PContext, n: PNode) = proc addCodeForGenerics(c: PContext, n: PNode) =
@ -282,6 +319,7 @@ proc myOpen(module: PSym): PPassContext =
c.semConstExpr = semConstExpr c.semConstExpr = semConstExpr
c.semExpr = semExpr c.semExpr = semExpr
c.semTryExpr = tryExpr c.semTryExpr = tryExpr
c.semTryConstExpr = tryConstExpr
c.semOperand = semOperand c.semOperand = semOperand
c.semConstBoolExpr = semConstBoolExpr c.semConstBoolExpr = semConstBoolExpr
c.semOverloadedCall = semOverloadedCall c.semOverloadedCall = semOverloadedCall

View file

@ -47,14 +47,14 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
var z: TCandidate var z: TCandidate
if sym == nil: return if sym == nil: return
initCandidate(best, sym, initialBinding, symScope) initCandidate(c, best, sym, initialBinding, symScope)
initCandidate(alt, sym, initialBinding, symScope) initCandidate(c, alt, sym, initialBinding, symScope)
best.state = csNoMatch best.state = csNoMatch
while sym != nil: while sym != nil:
if sym.kind in filter: if sym.kind in filter:
determineType(c, sym) determineType(c, sym)
initCandidate(z, sym, initialBinding, o.lastOverloadScope) initCandidate(c, z, sym, initialBinding, o.lastOverloadScope)
z.calleeSym = sym z.calleeSym = sym
matches(c, n, orig, z) matches(c, n, orig, z)
if errors != nil: if errors != nil:
@ -199,15 +199,15 @@ proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
var m: TCandidate var m: TCandidate
initCandidate(m, f) initCandidate(c, m, f)
result = paramTypesMatch(c, m, f, a, arg, nil) result = paramTypesMatch(m, f, a, arg, nil)
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 convertTo*(c: PContext, f: PType, n: PNode): PNode =
var m: TCandidate var m: TCandidate
initCandidate(m, f) initCandidate(c, m, f)
result = paramTypesMatch(c, m, f, n.typ, n, nil) result = paramTypesMatch(m, f, n.typ, n, nil)
if m.genericConverter and result != nil: if m.genericConverter and result != nil:
instGenericConvertersArg(c, result, m) instGenericConvertersArg(c, result, m)
@ -243,9 +243,9 @@ proc explicitGenericInstError(n: PNode): PNode =
result = n result = n
proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
var x: TCandidate var m: TCandidate
initCandidate(x, s, n) initCandidate(c, m, s, n)
var newInst = generateInstance(c, s, x.bindings, n.info) var newInst = generateInstance(c, s, m.bindings, n.info)
markUsed(n, s) markUsed(n, s)
result = newSymNode(newInst, n.info) result = newSymNode(newInst, n.info)

View file

@ -75,6 +75,7 @@ type
semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semTryExpr*: proc (c: PContext, n: PNode,flags: TExprFlags = {}, semTryExpr*: proc (c: PContext, n: PNode,flags: TExprFlags = {},
bufferErrors = false): PNode {.nimcall.} bufferErrors = false): PNode {.nimcall.}
semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.}
semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
@ -213,14 +214,40 @@ proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc) let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
return newSymNode(sym, info) return newSymNode(sym, info)
proc newTypeS(kind: TTypeKind, c: PContext): PType = proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
result = newType(kind, getCurrOwner()) result = newTypeS(tyFromExpr, c)
result.n = n
proc newTypeWithSons*(c: PContext, kind: TTypeKind, proc newTypeWithSons*(c: PContext, kind: TTypeKind,
sons: seq[PType]): PType = sons: seq[PType]): PType =
result = newType(kind, getCurrOwner()) result = newType(kind, getCurrOwner())
result.sons = sons result.sons = sons
proc makeStaticExpr*(c: PContext, n: PNode): PNode =
result = newNodeI(nkStaticExpr, n.info)
result.sons = @[n]
result.typ = newTypeWithSons(c, tyStatic, @[n.typ])
proc makeAndType*(c: PContext, t1, t2: PType): PType =
result = newTypeS(tyAnd, c)
result.sons = @[t1, t2]
propagateToOwner(result, t1)
propagateToOwner(result, t2)
proc makeOrType*(c: PContext, t1, t2: PType): PType =
result = newTypeS(tyOr, c)
result.sons = @[t1, t2]
propagateToOwner(result, t1)
propagateToOwner(result, t2)
proc makeNotType*(c: PContext, t1: PType): PType =
result = newTypeS(tyNot, c)
result.sons = @[t1]
propagateToOwner(result, t1)
proc newTypeS(kind: TTypeKind, c: PContext): PType =
result = newType(kind, getCurrOwner())
proc errorType*(c: PContext): PType = proc errorType*(c: PContext): PType =
## creates a type representing an error state ## creates a type representing an error state
result = newTypeS(tyError, c) result = newTypeS(tyError, c)

View file

@ -9,6 +9,7 @@
## This module implements destructors. ## This module implements destructors.
# included from sem.nim
# special marker values that indicates that we are # special marker values that indicates that we are
# 1) AnalyzingDestructor: currently analyzing the type for destructor # 1) AnalyzingDestructor: currently analyzing the type for destructor
@ -25,10 +26,22 @@ var
destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo()) destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo())
rangeDestructorProc*: PSym rangeDestructorProc*: PSym
proc instantiateDestructor(c: PContext, typ: PType): bool proc instantiateDestructor(c: PContext, typ: PType): PType
proc doDestructorStuff(c: PContext, s: PSym, n: PNode) = proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
let t = s.typ.sons[1].skipTypes({tyVar}) var t = s.typ.sons[1].skipTypes({tyVar})
if t.kind == tyGenericInvokation:
for i in 1 .. <t.sonsLen:
if t.sons[i].kind != tyGenericParam:
localError(n.info, errDestructorNotGenericEnough)
return
t = t.base
elif t.kind == tyCompositeTypeClass:
t = t.base
if t.kind != tyGenericBody:
localError(n.info, errDestructorNotGenericEnough)
return
t.destructor = s t.destructor = s
# automatically insert calls to base classes' destructors # automatically insert calls to base classes' destructors
if n.sons[bodyPos].kind != nkEmpty: if n.sons[bodyPos].kind != nkEmpty:
@ -36,15 +49,17 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
# when inheriting directly from object # when inheriting directly from object
# there will be a single nil son # there will be a single nil son
if t.sons[i] == nil: continue if t.sons[i] == nil: continue
if instantiateDestructor(c, t.sons[i]): let destructableT = instantiateDestructor(c, t.sons[i])
if destructableT != nil:
n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[ n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
useSym(t.sons[i].destructor), useSym(destructableT.destructor),
n.sons[paramsPos][1][0]])) n.sons[paramsPos][1][0]]))
proc destroyField(c: PContext, field: PSym, holder: PNode): PNode = proc destroyField(c: PContext, field: PSym, holder: PNode): PNode =
if instantiateDestructor(c, field.typ): let destructableT = instantiateDestructor(c, field.typ)
if destructableT != nil:
result = newNode(nkCall, field.info, @[ result = newNode(nkCall, field.info, @[
useSym(field.typ.destructor), useSym(destructableT.destructor),
newNode(nkDotExpr, field.info, @[holder, useSym(field)])]) newNode(nkDotExpr, field.info, @[holder, useSym(field)])])
proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode = proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
@ -105,26 +120,35 @@ proc generateDestructor(c: PContext, t: PType): PNode =
# base classes' destructors will be automatically called by # base classes' destructors will be automatically called by
# semProcAux for both auto-generated and user-defined destructors # semProcAux for both auto-generated and user-defined destructors
proc instantiateDestructor(c: PContext, typ: PType): bool = proc instantiateDestructor(c: PContext, typ: PType): PType =
# returns true if the type already had a user-defined # returns nil if a variable of type `typ` doesn't require a
# destructor or if the compiler generated a default # destructor. Otherwise, returns the type, which holds the
# member-wise one # destructor that must be used for the varialbe.
var t = skipTypes(typ, {tyConst, tyMutable}) # The destructor is either user-defined or automatically
# generated by the compiler in a member-wise fashion.
var t = skipTypes(typ, {tyConst, tyMutable}).skipGenericAlias
let typeHoldingUserDefinition = if t.kind == tyGenericInst: t.base
else: t
if t.destructor != nil: if typeHoldingUserDefinition.destructor != nil:
# XXX: This is not entirely correct for recursive types, but we need # XXX: This is not entirely correct for recursive types, but we need
# it temporarily to hide the "destroy is already defined" problem # it temporarily to hide the "destroy is already defined" problem
return t.destructor notin [analyzingDestructor, destructorIsTrivial] if typeHoldingUserDefinition.destructor notin
[analyzingDestructor, destructorIsTrivial]:
return typeHoldingUserDefinition
else:
return nil
t = t.skipTypes({tyGenericInst})
case t.kind case t.kind
of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs: of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
if instantiateDestructor(c, t.sons[0]): if instantiateDestructor(c, t.sons[0]) != nil:
if rangeDestructorProc == nil: if rangeDestructorProc == nil:
rangeDestructorProc = searchInScopes(c, getIdent"nimDestroyRange") rangeDestructorProc = searchInScopes(c, getIdent"nimDestroyRange")
t.destructor = rangeDestructorProc t.destructor = rangeDestructorProc
return true return t
else: else:
return false return nil
of tyTuple, tyObject: of tyTuple, tyObject:
t.destructor = analyzingDestructor t.destructor = analyzingDestructor
let generated = generateDestructor(c, t) let generated = generateDestructor(c, t)
@ -139,21 +163,21 @@ proc instantiateDestructor(c: PContext, typ: PType): bool =
emptyNode, emptyNode,
newNode(nkIdentDefs, i, @[ newNode(nkIdentDefs, i, @[
newIdentNode(destructorParam, i), newIdentNode(destructorParam, i),
useSym(t.sym), symNodeFromType(c, makeVarType(c, t), t.sym.info),
emptyNode]), emptyNode]),
]), ]),
newNode(nkPragma, i, @[destructorPragma]), newNode(nkPragma, i, @[destructorPragma]),
emptyNode, emptyNode,
generated generated
]) ])
discard semProc(c, fullDef) let semantizedDef = semProc(c, fullDef)
internalAssert t.destructor != nil t.destructor = semantizedDef[namePos].sym
return true return t
else: else:
t.destructor = destructorIsTrivial t.destructor = destructorIsTrivial
return false return nil
else: else:
return false return nil
proc insertDestructors(c: PContext, proc insertDestructors(c: PContext,
varSection: PNode): tuple[outer, inner: PNode] = varSection: PNode): tuple[outer, inner: PNode] =
@ -180,8 +204,10 @@ proc insertDestructors(c: PContext,
varTyp = varId.sym.typ varTyp = varId.sym.typ
info = varId.info info = varId.info
if varTyp != nil and instantiateDestructor(c, varTyp) and if varTyp == nil or sfGlobal in varId.sym.flags: continue
sfGlobal notin varId.sym.flags: let destructableT = instantiateDestructor(c, varTyp)
if destructableT != nil:
var tryStmt = newNodeI(nkTryStmt, info) var tryStmt = newNodeI(nkTryStmt, info)
if j < totalVars - 1: if j < totalVars - 1:
@ -202,7 +228,7 @@ proc insertDestructors(c: PContext,
tryStmt.addSon( tryStmt.addSon(
newNode(nkFinally, info, @[ newNode(nkFinally, info, @[
semStmt(c, newNode(nkCall, info, @[ semStmt(c, newNode(nkCall, info, @[
useSym(varTyp.destructor), useSym(destructableT.destructor),
useSym(varId.sym)]))])) useSym(varId.sym)]))]))
result.outer = newNodeI(nkStmtList, info) result.outer = newNodeI(nkStmtList, info)

View file

@ -102,7 +102,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
# if a proc accesses a global variable, it is not side effect free: # if a proc accesses a global variable, it is not side effect free:
if sfGlobal in s.flags: if sfGlobal in s.flags:
incl(c.p.owner.flags, sfSideEffect) incl(c.p.owner.flags, sfSideEffect)
elif s.kind == skParam and s.typ.kind == tyExpr and s.typ.n != nil: elif s.kind == skParam and s.typ.kind == tyStatic and s.typ.n != nil:
# XXX see the hack in sigmatch.nim ... # XXX see the hack in sigmatch.nim ...
return s.typ.n return s.typ.n
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
@ -111,7 +111,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
# var len = 0 # but won't be called # var len = 0 # but won't be called
# genericThatUsesLen(x) # marked as taking a closure? # genericThatUsesLen(x) # marked as taking a closure?
of skGenericParam: of skGenericParam:
if s.typ.kind == tyExpr: if s.typ.kind == tyStatic:
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
result.typ = s.typ result.typ = s.typ
elif s.ast != nil: elif s.ast != nil:
@ -142,7 +142,7 @@ proc checkConversionBetweenObjects(castDest, src: PType): TConvStatus =
const const
IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyUInt64} IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyUInt64}
proc checkConvertible(castDest, src: PType): TConvStatus = proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
result = convOK result = convOK
if sameType(castDest, src) and castDest.sym == src.sym: if sameType(castDest, src) and castDest.sym == src.sym:
# don't annoy conversions that may be needed on another processor: # don't annoy conversions that may be needed on another processor:
@ -163,7 +163,7 @@ proc checkConvertible(castDest, src: PType): TConvStatus =
# accept conversion between integral types # accept conversion between integral types
else: else:
# we use d, s here to speed up that operation a bit: # we use d, s here to speed up that operation a bit:
case cmpTypes(d, s) case cmpTypes(c, d, s)
of isNone, isGeneric: of isNone, isGeneric:
if not compareTypes(castDest, src, dcEqIgnoreDistinct): if not compareTypes(castDest, src, dcEqIgnoreDistinct):
result = convNotLegal result = convNotLegal
@ -202,7 +202,7 @@ proc semConv(c: PContext, n: PNode): PNode =
var op = result.sons[1] var op = result.sons[1]
if not isSymChoice(op): if not isSymChoice(op):
let status = checkConvertible(result.typ, op.typ) let status = checkConvertible(c, result.typ, op.typ)
case status case status
of convOK: nil of convOK: nil
of convNotNeedeed: of convNotNeedeed:
@ -213,7 +213,7 @@ proc semConv(c: PContext, n: PNode): PNode =
else: else:
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(result.typ, it.typ) let status = checkConvertible(c, result.typ, it.typ)
if status == convOK: if status == convOK:
markUsed(n, it.sym) markUsed(n, it.sym)
markIndirect(c, it.sym) markIndirect(c, it.sym)
@ -239,7 +239,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
localError(n.info, errXExpectsTypeOrValue, opToStr[m]) localError(n.info, errXExpectsTypeOrValue, opToStr[m])
else: else:
n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
var typ = skipTypes(n.sons[1].typ, abstractVarRange) var typ = skipTypes(n.sons[1].typ, abstractVarRange+{tyTypeDesc})
case typ.kind case typ.kind
of tySequence, tyString, tyOpenArray, tyVarargs: of tySequence, tyString, tyOpenArray, tyVarargs:
n.typ = getSysType(tyInt) n.typ = getSysType(tyInt)
@ -249,8 +249,10 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
# do not skip the range! # do not skip the range!
n.typ = n.sons[1].typ.skipTypes(abstractVar) n.typ = n.sons[1].typ.skipTypes(abstractVar)
of tyGenericParam: of tyGenericParam:
# leave it for now, it will be resolved in semtypinst # prepare this for resolving in semtypinst:
n.typ = getSysType(tyInt) # we must use copyTree here in order to avoid creating a cycle
# that could easily turn into an infinite recursion in semtypinst
n.typ = makeTypeFromExpr(c, n.copyTree)
else: else:
localError(n.info, errInvalidArgForX, opToStr[m]) localError(n.info, errInvalidArgForX, opToStr[m])
result = n result = n
@ -301,7 +303,7 @@ proc semOf(c: PContext, n: PNode): PNode =
proc isOpImpl(c: PContext, n: PNode): PNode = proc isOpImpl(c: PContext, n: PNode): PNode =
internalAssert n.sonsLen == 3 and internalAssert n.sonsLen == 3 and
n[1].typ != nil and n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType} n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
let t1 = n[1].typ.skipTypes({tyTypeDesc}) let t1 = n[1].typ.skipTypes({tyTypeDesc})
@ -324,15 +326,15 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
case t2.kind case t2.kind
of tyTypeClasses: of tyTypeClasses:
var m: TCandidate var m: TCandidate
initCandidate(m, t2) initCandidate(c, m, t2)
match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil
of tyOrdinal: of tyOrdinal:
var m: TCandidate var m: TCandidate
initCandidate(m, t2) initCandidate(c, m, t2)
match = isOrdinalType(t1) match = isOrdinalType(t1)
of tySequence, tyArray, tySet: of tySequence, tyArray, tySet:
var m: TCandidate var m: TCandidate
initCandidate(m, t2) initCandidate(c, m, t2)
match = typeRel(m, t2, t1) != isNone match = typeRel(m, t2, t1) != isNone
else: else:
match = sameType(t1, t2) match = sameType(t1, t2)
@ -668,6 +670,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
else: else:
result = semOverloadedCall(c, n, nOrig, result = semOverloadedCall(c, n, nOrig,
{skProc, skMethod, skConverter, skMacro, skTemplate}) {skProc, skMethod, skConverter, skMacro, skTemplate})
if result != nil: if result != nil:
if result.sons[0].kind != nkSym: if result.sons[0].kind != nkSym:
internalError("semOverloadedCallAnalyseEffects") internalError("semOverloadedCallAnalyseEffects")
@ -706,7 +709,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
if t != nil and t.kind == tyProc: if t != nil and t.kind == tyProc:
# This is a proc variable, apply normal overload resolution # This is a proc variable, apply normal overload resolution
var m: TCandidate var m: TCandidate
initCandidate(m, t) initCandidate(c, m, t)
matches(c, n, nOrig, m) matches(c, n, nOrig, m)
if m.state != csMatch: if m.state != csMatch:
if c.inCompilesContext > 0: if c.inCompilesContext > 0:
@ -939,7 +942,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
let tParam = tbody.sons[s] let tParam = tbody.sons[s]
if tParam.sym.name == i: if tParam.sym.name == i:
let rawTyp = ty.sons[s + 1] let rawTyp = ty.sons[s + 1]
if rawTyp.kind == tyExpr: if rawTyp.kind == tyStatic:
return rawTyp.n return rawTyp.n
else: else:
let foundTyp = makeTypeDesc(c, rawTyp) let foundTyp = makeTypeDesc(c, rawTyp)
@ -1163,8 +1166,8 @@ 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 lhs.sym.typ.kind == tyGenericParam: if lhs.sym.typ.isMetaType and lhs.sym.typ.kind != tyTypeDesc:
if matchTypeClass(lhs.typ, rhs.typ): 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
c.p.resultSym.typ = rhs.typ c.p.resultSym.typ = rhs.typ
@ -1884,7 +1887,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkBind: of nkBind:
message(n.info, warnDeprecated, "bind") message(n.info, warnDeprecated, "bind")
result = semExpr(c, n.sons[0], flags) result = semExpr(c, n.sons[0], flags)
of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy: of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy:
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
result.typ = makeTypeDesc(c, typ) result.typ = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info) #result = symNodeFromType(c, typ, n.info)
@ -1945,6 +1948,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# type parameters: partial generic specialization # type parameters: partial generic specialization
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
result = explicitGenericInstantiation(c, n, s) result = explicitGenericInstantiation(c, n, s)
elif s != nil and s.kind in {skType}:
result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
else: else:
result = semArrayAccess(c, n, flags) result = semArrayAccess(c, n, flags)
of nkCurlyExpr: of nkCurlyExpr:

View file

@ -230,6 +230,7 @@ discard """
""" """
proc evalIs(n, a: PNode): PNode = proc evalIs(n, a: PNode): PNode =
# XXX: This should use the standard isOpImpl
internalAssert a.kind == nkSym and a.sym.kind == skType internalAssert a.kind == nkSym and a.sym.kind == skType
internalAssert n.sonsLen == 3 and internalAssert n.sonsLen == 3 and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType} n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
@ -251,7 +252,7 @@ proc evalIs(n, a: PNode): PNode =
else: else:
# XXX semexprs.isOpImpl is slightly different and requires a context. yay. # XXX semexprs.isOpImpl is slightly different and requires a context. yay.
let t2 = n[2].typ let t2 = n[2].typ
var match = if t2.kind == tyTypeClass: matchTypeClass(t2, t1) var match = if t2.kind == tyTypeClass: true
else: sameType(t1, t2) else: sameType(t1, t2)
result = newIntNode(nkIntLit, ord(match)) result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ result.typ = n.typ
@ -612,7 +613,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of skType: of skType:
result = newSymNodeTypeDesc(s, n.info) result = newSymNodeTypeDesc(s, n.info)
of skGenericParam: of skGenericParam:
if s.typ.kind == tyExpr: if s.typ.kind == tyStatic:
if s.typ.n != nil:
result = s.typ.n result = s.typ.n
result.typ = s.typ.sons[0] result.typ = s.typ.sons[0]
else: else:

View file

@ -17,11 +17,6 @@
# included from sem.nim # included from sem.nim
type
TSemGenericFlag = enum
withinBind, withinTypeDesc, withinMixin
TSemGenericFlags = set[TSemGenericFlag]
proc getIdentNode(n: PNode): PNode = proc getIdentNode(n: PNode): PNode =
case n.kind case n.kind
of nkPostfix: result = getIdentNode(n.sons[1]) of nkPostfix: result = getIdentNode(n.sons[1])
@ -31,8 +26,6 @@ proc getIdentNode(n: PNode): PNode =
illFormedAst(n) illFormedAst(n)
result = n result = n
proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var TIntSet): PNode
proc semGenericStmtScope(c: PContext, n: PNode, proc semGenericStmtScope(c: PContext, n: PNode,
flags: TSemGenericFlags, flags: TSemGenericFlags,
ctx: var TIntSet): PNode = ctx: var TIntSet): PNode =

View file

@ -20,7 +20,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
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
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyExpr}+tyTypeClasses: if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses:
continue continue
var s = newSym(skType, q.name, getCurrOwner(), q.info) var s = newSym(skType, q.name, getCurrOwner(), q.info)
s.flags = s.flags + {sfUsed, sfFromGeneric} s.flags = s.flags + {sfUsed, sfFromGeneric}
@ -47,7 +47,7 @@ proc sameInstantiation(a, b: TInstantiation): bool =
if a.concreteTypes.len == b.concreteTypes.len: if a.concreteTypes.len == b.concreteTypes.len:
for i in 0..a.concreteTypes.high: for i in 0..a.concreteTypes.high:
if not compareTypes(a.concreteTypes[i], b.concreteTypes[i], if not compareTypes(a.concreteTypes[i], b.concreteTypes[i],
flags = {TypeDescExactMatch}): return flags = {ExactTypeDescValues}): return
result = true result = true
proc genericCacheGet(genericSym: PSym, entry: TInstantiation): PSym = proc genericCacheGet(genericSym: PSym, entry: TInstantiation): PSym =
@ -131,134 +131,20 @@ proc sideEffectsCheck(c: PContext, s: PSym) =
s.ast.sons[genericParamsPos].kind == nkEmpty: s.ast.sons[genericParamsPos].kind == nkEmpty:
c.threadEntries.add(s) c.threadEntries.add(s)
proc lateInstantiateGeneric(c: PContext, invocation: PType, info: TLineInfo): PType = proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
internalAssert invocation.kind == tyGenericInvokation allowMetaTypes = false): PType =
let cacheHit = searchInstTypes(invocation)
if cacheHit != nil:
result = cacheHit
else:
let s = invocation.sons[0].sym
let oldScope = c.currentScope
c.currentScope = s.typScope
openScope(c)
pushInfoContext(info)
for i in 0 .. <s.typ.n.sons.len:
let genericParam = s.typ.n[i].sym
let symKind = if genericParam.typ.kind == tyExpr: skConst
else: skType
var boundSym = newSym(symKind, s.typ.n[i].sym.name, s, info)
boundSym.typ = invocation.sons[i+1].skipTypes({tyExpr})
boundSym.ast = invocation.sons[i+1].n
addDecl(c, boundSym)
# XXX: copyTree would have been unnecessary here if semTypeNode
# didn't modify its input parameters. Currently, it does modify
# at least the record lists of the passed object and tuple types
var instantiated = semTypeNode(c, copyTree(s.ast[2]), nil)
popInfoContext()
closeScope(c)
c.currentScope = oldScope
if instantiated != nil:
result = invocation
result.kind = tyGenericInst
result.sons.add instantiated
cacheTypeInst result
proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType =
when oUseLateInstantiation:
lateInstantiateGeneric(c, header, info)
else:
var cl: TReplTypeVars var cl: TReplTypeVars
initIdTable(cl.symMap) initIdTable(cl.symMap)
initIdTable(cl.typeMap) initIdTable(cl.typeMap)
initIdTable(cl.localCache)
cl.info = info cl.info = info
cl.c = c cl.c = c
cl.allowMetaTypes = allowMetaTypes
result = replaceTypeVarsT(cl, header) result = replaceTypeVarsT(cl, header)
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 fixupProcType(c: PContext, genericType: PType,
inst: TInstantiation): PType =
# XXX: This is starting to look suspiciously like ReplaceTypeVarsT
# there are few apparent differences, but maybe the code could be
# moved over.
# * the code here uses the new genericSym.position property when
# doing lookups.
# * the handling of tyTypeDesc seems suspicious in ReplaceTypeVarsT
# typedesc params were previously handled in the second pass of
# semParamList
# * void (nkEmpty) params doesn't seem to be stripped in ReplaceTypeVarsT
result = genericType
if result == nil: return
case genericType.kind
of tyGenericParam, tyTypeClasses:
result = inst.concreteTypes[genericType.sym.position]
of tyTypeDesc:
result = inst.concreteTypes[genericType.sym.position]
if tfUnresolved in genericType.flags:
result = result.sons[0]
of tyExpr:
result = inst.concreteTypes[genericType.sym.position]
of tyOpenArray, tyArray, tySet, tySequence, tyTuple, tyProc,
tyPtr, tyVar, tyRef, tyOrdinal, tyRange, tyVarargs:
if genericType.sons == nil: return
var head = 0
for i in 0 .. <genericType.sons.len:
let origType = genericType.sons[i]
var changed = fixupProcType(c, origType, inst)
if changed != genericType.sons[i]:
var changed = changed.skipIntLit
if result == genericType:
# the first detected change initializes the result
result = copyType(genericType, genericType.owner, false)
if genericType.n != nil:
result.n = copyTree(genericType.n)
# XXX: doh, we have to treat seq and arrays as special case
# because sometimes the `@` magic will be applied to an empty
# sequence having the type tySequence(tyEmpty)
if changed.kind == tyEmpty and
genericType.kind notin {tyArray, tySequence}:
if genericType.kind == tyProc and i == 0:
# return types of procs are overwritten with nil
changed = nil
else:
# otherwise, `empty` is just erased from the signature
result.sons[i..i] = []
if result.n != nil: result.n.sons[i..i] = []
continue
result.sons[head] = changed
if result.n != nil:
if result.n.kind == nkRecList:
for son in result.n.sons:
if son.typ == origType:
son.typ = changed
son.sym = copySym(son.sym, true)
son.sym.typ = changed
if result.n.kind == nkFormalParams:
if i != 0:
let origParam = result.n.sons[head].sym
var param = copySym(origParam)
param.typ = changed
param.ast = origParam.ast
result.n.sons[head] = newSymNode(param)
# won't be advanced on empty (void) nodes
inc head
of tyGenericInvokation:
result = newTypeWithSons(c, tyGenericInvokation, genericType.sons)
for i in 1 .. <genericType.sons.len:
result.sons[i] = fixupProcType(c, result.sons[i], inst)
result = instGenericContainer(c, getInfoContext(-1), result)
else: discard
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:
@ -281,14 +167,13 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
result.ast = n result.ast = n
pushOwner(result) pushOwner(result)
openScope(c) openScope(c)
if n.sons[genericParamsPos].kind == nkEmpty: internalAssert n.sons[genericParamsPos].kind != nkEmpty
internalError(n.info, "generateInstance")
n.sons[namePos] = newSymNode(result) n.sons[namePos] = newSymNode(result)
pushInfoContext(info) pushInfoContext(info)
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 = fixupProcType(c, fn.typ, entry[]) result.typ = generateTypeInstance(c, pt, info, fn.typ)
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:

View file

@ -32,7 +32,6 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode =
result.add(filename) result.add(filename)
result.add(line) result.add(line)
proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode = proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
let typ = operand.skipTypes({tyTypeDesc}) let typ = operand.skipTypes({tyTypeDesc})
case trait.sym.name.s.normalize case trait.sym.name.s.normalize
@ -50,11 +49,11 @@ proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
proc semTypeTraits(c: PContext, n: PNode): PNode = proc semTypeTraits(c: PContext, n: PNode): PNode =
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
let t = n.sons[1].typ let t = n.sons[1].typ
internalAssert t != nil internalAssert t != nil and t.kind == tyTypeDesc
if t.kind == tyTypeDesc and t.len == 0: if t.sonsLen > 0:
result = n # This is either a type known to sem or a typedesc
elif not containsGenericType(t): # param to a regular proc (again, known at instantiation)
result = evalTypeTrait(n[0], t, getCurrOwner()) result = evalTypeTrait(n[0], t, GetCurrOwner())
else: else:
# a typedesc variable, pass unmodified to evals # a typedesc variable, pass unmodified to evals
result = n result = n
@ -102,7 +101,7 @@ proc semLocals(c: PContext, n: PNode): PNode =
#if it.owner != c.p.owner: return result #if it.owner != c.p.owner: return result
if it.kind in skLocalVars and if it.kind in skLocalVars and
it.typ.skipTypes({tyGenericInst, tyVar}).kind notin it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
{tyVarargs, tyOpenArray, tyTypeDesc, tyExpr, tyStmt, tyEmpty}: {tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
var field = newSym(skField, it.name, getCurrOwner(), n.info) var field = newSym(skField, it.name, getCurrOwner(), n.info)
field.typ = it.typ.skipTypes({tyGenericInst, tyVar}) field.typ = it.typ.skipTypes({tyGenericInst, tyVar})

View file

@ -86,7 +86,7 @@ include semdestruct
proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}: if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}:
if instantiateDestructor(c, n.typ): if instantiateDestructor(c, n.typ) != nil:
localError(n.info, errGenerated, localError(n.info, errGenerated,
"usage of a type with a destructor in a non destructible context") "usage of a type with a destructor in a non destructible context")
# This still breaks too many things: # This still breaks too many things:
@ -740,10 +740,6 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
# like: mydata.seq # like: mydata.seq
rawAddSon(s.typ, newTypeS(tyEmpty, c)) rawAddSon(s.typ, newTypeS(tyEmpty, c))
s.ast = a s.ast = a
when oUseLateInstantiation:
var body: PType = nil
s.typScope = c.currentScope.parent
else:
inc c.inGenericContext inc c.inGenericContext
var body = semTypeNode(c, a.sons[2], nil) var body = semTypeNode(c, a.sons[2], nil)
dec c.inGenericContext dec c.inGenericContext
@ -1041,8 +1037,12 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
# for DLL generation it is annoying to check for sfImportc! # for DLL generation it is annoying to check for sfImportc!
if sfBorrow in s.flags: if sfBorrow in s.flags:
localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
if n.sons[genericParamsPos].kind == nkEmpty: let usePseudoGenerics = kind in {skMacro, skTemplate}
paramsTypeCheck(c, s.typ) # Macros and Templates can have generic parameters, but they are
# only used for overload resolution (there is no instantiation of
# the symbol, so we must process the body now)
if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics:
if not usePseudoGenerics: paramsTypeCheck(c, s.typ)
pushProcCon(c, s) pushProcCon(c, s)
maybeAddResult(c, s, n) maybeAddResult(c, s, n)
if sfImportc notin s.flags: if sfImportc notin s.flags:

View file

@ -421,6 +421,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
else: else:
s = semIdentVis(c, skTemplate, n.sons[0], {}) s = semIdentVis(c, skTemplate, n.sons[0], {})
# check parameter list: # check parameter list:
s.scope = c.currentScope
pushOwner(s) pushOwner(s)
openScope(c) openScope(c)
n.sons[namePos] = newSymNode(s, n.sons[namePos].info) n.sons[namePos] = newSymNode(s, n.sons[namePos].info)

View file

@ -18,7 +18,7 @@ proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
if result.kind == tyForward: result.kind = kind if result.kind == tyForward: result.kind = kind
proc newConstraint(c: PContext, k: TTypeKind): PType = proc newConstraint(c: PContext, k: TTypeKind): PType =
result = newTypeS(tyTypeClass, c) result = newTypeS(tyBuiltInTypeClass, c)
result.addSonSkipIntLit(newTypeS(k, c)) result.addSonSkipIntLit(newTypeS(k, c))
proc semEnum(c: PContext, n: PNode, prev: PType): PType = proc semEnum(c: PContext, n: PNode, prev: PType): PType =
@ -186,6 +186,11 @@ 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)
@ -194,19 +199,35 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
if isRange(n[1]): indx = semRangeAux(c, n[1], nil) if isRange(n[1]): indx = semRangeAux(c, n[1], nil)
else: else:
let e = semExprWithType(c, n.sons[1], {efDetermineType}) let e = semExprWithType(c, n.sons[1], {efDetermineType})
if e.kind in {nkIntLit..nkUInt64Lit}: if e.typ.kind == tyFromExpr:
indx = e.typ
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 == tyExpr: elif e.kind == nkSym and e.typ.kind == tyStatic:
if e.sym.ast != nil: return semArray(c, e.sym.ast, nil) if e.sym.ast != nil: return semArray(c, e.sym.ast, nil)
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 = e.typ
elif e.kind in nkCallKinds and hasGenericArguments(e):
if not isOrdinalType(e.typ):
localError(n[1].info, errOrdinalTypeExpected)
# This is an int returning call, depending on an
# yet unknown generic param (see tgenericshardcases).
# We are going to construct a range type that will be
# properly filled-out in semtypinst (see how tyStaticExpr
# is handled there).
let intType = getSysType(tyInt)
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)
if indx.kind == tyGenericInst: indx = lastSon(indx) if indx.kind == tyGenericInst: indx = lastSon(indx)
if indx.kind notin {tyGenericParam, tyExpr}: if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
if not isOrdinalType(indx): if not isOrdinalType(indx):
localError(n.sons[1].info, errOrdinalTypeExpected) localError(n.sons[1].info, errOrdinalTypeExpected)
elif enumHasHoles(indx): elif enumHasHoles(indx):
@ -581,7 +602,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
incl(result.flags, tfFinal) incl(result.flags, tfFinal)
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
if kind == skMacro and param.typ.kind != tyTypeDesc: if kind == skMacro and param.typ.kind notin {tyTypeDesc, tyStatic}:
# within a macro, every param has the type PNimrodNode! # within a macro, every param has the type PNimrodNode!
# and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}: # and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}:
let nn = getSysSym"PNimrodNode" let nn = getSysSym"PNimrodNode"
@ -593,18 +614,22 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
let typedescId = getIdent"typedesc" let typedescId = getIdent"typedesc"
template shouldHaveMeta(t) =
InternalAssert tfHasMeta in t.flags
# result.lastSon.flags.incl tfHasMeta
proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramType: PType, paramName: string, paramType: PType, paramName: string,
info: TLineInfo, anon = false): PType = info: TLineInfo, anon = false): PType =
if procKind in {skMacro, skTemplate}: if paramType == nil: return # (e.g. proc return type)
# generic param types in macros and templates affect overload
# resolution, but don't work as generic params when it comes
# to proc instantiation. We don't need to lift such params here.
return
proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType =
let finalTypId = if typId != nil: typId let finalTypId = if typId != nil: typId
else: getIdent(paramName & ":type") else: getIdent(paramName & ":type")
if genericParams == nil:
# This happens with anonymous proc types appearing in signatures
# XXX: we need to lift these earlier
return
# is this a bindOnce type class already present in the param list? # is this a bindOnce type class already present in the param list?
for i in countup(0, genericParams.len - 1): for i in countup(0, genericParams.len - 1):
if genericParams.sons[i].sym.name.id == finalTypId.id: if genericParams.sons[i].sym.name.id == finalTypId.id:
@ -615,6 +640,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
var s = newSym(skType, finalTypId, owner, info) var s = newSym(skType, finalTypId, owner, info)
if typId == nil: s.flags.incl(sfAnon) if typId == nil: s.flags.incl(sfAnon)
s.linkTo(typeClass) s.linkTo(typeClass)
typeClass.flags.incl tfImplicitTypeParam
s.position = genericParams.len s.position = genericParams.len
genericParams.addSon(newSymNode(s)) genericParams.addSon(newSymNode(s))
result = typeClass result = typeClass
@ -631,34 +657,38 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
addImplicitGenericImpl(e, paramTypId) addImplicitGenericImpl(e, paramTypId)
case paramType.kind: case paramType.kind:
of tyExpr: of tyAnything:
if paramType.sonsLen == 0:
# proc(a, b: expr)
# no constraints, treat like generic param
result = addImplicitGeneric(newTypeS(tyGenericParam, c)) result = addImplicitGeneric(newTypeS(tyGenericParam, c))
else:
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
result = addImplicitGeneric(c.newTypeWithSons(tyExpr, paramType.sons)) result = addImplicitGeneric(c.newTypeWithSons(tyStatic, paramType.sons))
result.flags.incl tfHasStatic
of tyTypeDesc: of tyTypeDesc:
if tfUnresolved notin paramType.flags: if tfUnresolved notin paramType.flags:
# naked typedescs are not bindOnce types # naked typedescs are not bindOnce types
if paramType.sonsLen == 0 and paramTypId != nil and if paramType.sonsLen == 0 and paramTypId != nil and
paramTypId.id == typedescId.id: paramTypId = nil paramTypId.id == typedescId.id: paramTypId = nil
result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons)) result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons))
of tyDistinct: of tyDistinct:
if paramType.sonsLen == 1: if paramType.sonsLen == 1:
# disable the bindOnce behavior for the type class # disable the bindOnce behavior for the type class
result = liftingWalk(paramType.sons[0], true) result = liftingWalk(paramType.sons[0], true)
of tySequence, tySet, tyArray, tyOpenArray:
of tySequence, tySet, tyArray, tyOpenArray,
tyVar, tyPtr, tyRef, tyProc:
# XXX: this is a bit strange, but proc(s: seq) # XXX: this is a bit strange, but proc(s: seq)
# produces tySequence(tyGenericParam, null). # produces tySequence(tyGenericParam, null).
# This also seems to be true when creating aliases # This also seems to be true when creating aliases
# like: type myseq = distinct seq. # like: type myseq = distinct seq.
# Maybe there is another better place to associate # Maybe there is another better place to associate
# the seq type class with the seq identifier. # the seq type class with the seq identifier.
if paramType.lastSon == nil: if paramType.kind == tySequence and paramType.lastSon == nil:
let typ = c.newTypeWithSons(tyTypeClass, @[newTypeS(paramType.kind, c)]) let typ = c.newTypeWithSons(tyBuiltInTypeClass,
@[newTypeS(paramType.kind, c)])
result = addImplicitGeneric(typ) result = addImplicitGeneric(typ)
else: else:
for i in 0 .. <paramType.sons.len: for i in 0 .. <paramType.sons.len:
@ -666,29 +696,55 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
if lifted != nil: if lifted != nil:
paramType.sons[i] = lifted paramType.sons[i] = lifted
result = paramType result = paramType
of tyGenericBody: of tyGenericBody:
# type Foo[T] = object result = newTypeS(tyGenericInvokation, c)
# proc x(a: Foo, b: Foo) result.rawAddSon(paramType)
var typ = newTypeS(tyTypeClass, c) for i in 0 .. paramType.sonsLen - 2:
typ.addSonSkipIntLit(paramType) result.rawAddSon newTypeS(tyAnything, c)
result = addImplicitGeneric(typ) # result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
result = instGenericContainer(c, paramType.sym.info, result,
allowMetaTypes = true)
result.lastSon.shouldHaveMeta
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
result = addImplicitGeneric(result)
of tyGenericInst: of tyGenericInst:
for i in 1 .. (paramType.sons.len - 2): for i in 1 .. (paramType.sons.len - 2):
var lifted = liftingWalk(paramType.sons[i]) var lifted = liftingWalk(paramType.sons[i])
if lifted != nil: if lifted != nil:
paramType.sons[i] = lifted paramType.sons[i] = lifted
result = paramType result = paramType
result.lastSon.shouldHaveMeta
let liftBody = liftingWalk(paramType.lastSon)
if liftBody != nil:
result = liftBody
result.shouldHaveMeta
of tyGenericInvokation:
for i in 1 .. <paramType.sonsLen:
let lifted = liftingWalk(paramType.sons[i])
if lifted != nil: paramType.sons[i] = lifted
let expanded = instGenericContainer(c, info, paramType,
allowMetaTypes = true)
result = liftingWalk(expanded)
of tyTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
of tyExpr:
if procKind notin {skMacro, skTemplate}:
result = addImplicitGeneric(newTypeS(tyAnything, c))
of tyGenericParam:
if tfGenericTypeParam in paramType.flags and false:
if paramType.sonsLen > 0:
result = liftingWalk(paramType.lastSon)
else:
result = addImplicitGeneric(newTypeS(tyAnything, c))
if paramType.lastSon.kind == tyTypeClass:
result = paramType
result.kind = tyParametricTypeClass
result = addImplicitGeneric(copyType(result,
getCurrOwner(), false))
elif result != nil:
result.kind = tyGenericInvokation
result.sons.setLen(result.sons.len - 1)
of tyTypeClass:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), false))
else: nil else: nil
# result = liftingWalk(paramType) # result = liftingWalk(paramType)
@ -745,7 +801,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if not containsGenericType(typ): if not containsGenericType(typ):
def = fitNode(c, typ, def) def = fitNode(c, typ, def)
if not (hasType or hasDefault): if not (hasType or hasDefault):
typ = newTypeS(tyExpr, c) let tdef = if kind in {skTemplate, skMacro}: tyExpr else: tyAnything
typ = newTypeS(tdef, c)
if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
for j in countup(0, length-3): for j in countup(0, length-3):
@ -804,22 +861,6 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
dec(c.p.nestedBlockCounter) dec(c.p.nestedBlockCounter)
proc semGenericParamInInvokation(c: PContext, n: PNode): PType = proc semGenericParamInInvokation(c: PContext, n: PNode): PType =
# XXX hack 1022 for generics ... would have been nice if the compiler had
# been designed with them in mind from start ...
when false:
if n.kind == nkSym:
# for generics we need to lookup the type var again:
var s = searchInScopes(c, n.sym.name)
if s != nil:
if s.kind == skType and s.typ != nil:
var t = n.sym.typ
echo "came here"
return t
else:
echo "s is crap:"
debug(s)
else:
echo "s is nil!!!!"
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 =
@ -842,7 +883,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
else: else:
internalAssert s.typ.kind == tyGenericBody internalAssert s.typ.kind == tyGenericBody
var m = newCandidate(s, n) 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:
@ -864,15 +905,13 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
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:
when oUseLateInstantiation: result = instGenericContainer(c, n.info, result,
result = lateInstantiateGeneric(c, result, n.info) allowMetaTypes = false)
else:
result = instGenericContainer(c, n, result)
proc semTypeExpr(c: PContext, n: PNode): PType = proc semTypeExpr(c: PContext, n: PNode): PType =
var n = semExprWithType(c, n, {efDetermineType}) var n = semExprWithType(c, n, {efDetermineType})
if n.kind == nkSym and n.sym.kind == skType: if n.typ.kind == tyTypeDesc:
result = n.sym.typ result = n.typ.base
else: else:
localError(n.info, errTypeExpected, n.renderTree) localError(n.info, errTypeExpected, n.renderTree)
@ -928,17 +967,20 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
localError(n.sons[2].info, errTypeExpected) localError(n.sons[2].info, errTypeExpected)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
else: else:
result = newTypeS(tyTypeClass, c) result = if op.id == ord(wAnd): makeAndType(c, t1, t2)
result.addSonSkipIntLit(t1) else: makeOrType(c, t1, t2)
result.addSonSkipIntLit(t2)
result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny)
result.flags.incl(tfHasMeta)
elif op.id == ord(wNot): elif op.id == ord(wNot):
checkSonsLen(n, 3) case n.len
of 3:
result = semTypeNode(c, n.sons[1], prev) result = semTypeNode(c, n.sons[1], prev)
if result.kind in NilableTypes and n.sons[2].kind == nkNilLit: if result.kind in NilableTypes and n.sons[2].kind == nkNilLit:
result = freshType(result, prev) result = freshType(result, prev)
result.flags.incl(tfNotNil) result.flags.incl(tfNotNil)
else:
LocalError(n.info, errGenerated, "invalid type")
of 2:
let negated = semTypeNode(c, n.sons[1], prev)
result = makeNotType(c, negated)
else: else:
localError(n.info, errGenerated, "invalid type") localError(n.info, errGenerated, "invalid type")
else: else:
@ -968,6 +1010,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
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 nkIdent, nkDotExpr, nkAccQuoted:
if n.kind == nkDotExpr:
let head = qualifiedLookUp(c, n[0], {checkAmbiguity, checkUndeclared})
if head.kind in {skType}:
var toBind = initIntSet()
var preprocessed = semGenericStmt(c, n, {}, toBind)
return makeTypeFromExpr(c, preprocessed)
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)
@ -1004,6 +1052,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev) of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
of nkVarTy: result = semVarType(c, n, prev) of nkVarTy: result = semVarType(c, n, prev)
of nkDistinctTy: result = semDistinct(c, n, prev) of nkDistinctTy: result = semDistinct(c, n, prev)
of nkStaticTy:
result = newOrPrevType(tyStatic, prev, c)
var base = semTypeNode(c, n.sons[0], nil)
result.rawAddSon(base)
result.flags.incl tfHasStatic
of nkProcTy, nkIteratorTy: of nkProcTy, nkIteratorTy:
if n.sonsLen == 0: if n.sonsLen == 0:
result = newConstraint(c, tyProc) result = newConstraint(c, tyProc)
@ -1088,10 +1141,6 @@ proc processMagicType(c: PContext, m: PSym) =
else: localError(m.info, errTypeExpected) else: localError(m.info, errTypeExpected)
proc semGenericConstraints(c: PContext, x: PType): PType = proc semGenericConstraints(c: PContext, x: PType): PType =
if x.kind in StructuralEquivTypes and (
sonsLen(x) == 0 or x.sons[0].kind in {tyGenericParam, tyEmpty}):
result = newConstraint(c, x.kind)
else:
result = newTypeWithSons(c, tyGenericParam, @[x]) result = newTypeWithSons(c, tyGenericParam, @[x])
proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
@ -1109,7 +1158,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
if constraint.kind != nkEmpty: if constraint.kind != nkEmpty:
typ = semTypeNode(c, constraint, nil) typ = semTypeNode(c, constraint, nil)
if typ.kind != tyExpr or typ.len == 0: if typ.kind != tyStatic or typ.len == 0:
if typ.kind == tyTypeDesc: if typ.kind == tyTypeDesc:
if typ.len == 0: if typ.len == 0:
typ = newTypeS(tyTypeDesc, c) typ = newTypeS(tyTypeDesc, c)
@ -1120,7 +1169,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
def = semConstExpr(c, def) def = semConstExpr(c, def)
if typ == nil: if typ == nil:
if def.typ.kind != tyTypeDesc: if def.typ.kind != tyTypeDesc:
typ = newTypeWithSons(c, tyExpr, @[def.typ]) typ = newTypeWithSons(c, tyStatic, @[def.typ])
else: else:
if not containsGenericType(def.typ): if not containsGenericType(def.typ):
def = fitNode(c, typ, def) def = fitNode(c, typ, def)
@ -1128,6 +1177,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
if typ == nil: if typ == nil:
typ = newTypeS(tyGenericParam, c) typ = newTypeS(tyGenericParam, c)
typ.flags.incl tfGenericTypeParam
for j in countup(0, L-3): for j in countup(0, L-3):
let finalType = if j == 0: typ let finalType = if j == 0: typ
else: copyType(typ, typ.owner, false) else: copyType(typ, typ.owner, false)
@ -1136,7 +1187,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
# 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 = case finalType.kind
of tyExpr: 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

@ -11,6 +11,9 @@
import ast, astalgo, msgs, types, magicsys, semdata, renderer import ast, astalgo, msgs, types, magicsys, semdata, renderer
const
tfInstClearedFlags = {tfHasMeta}
proc checkPartialConstructedType(info: TLineInfo, t: PType) = proc checkPartialConstructedType(info: TLineInfo, t: PType) =
if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
localError(info, errInvalidPragmaX, "acyclic") localError(info, errInvalidPragmaX, "acyclic")
@ -19,12 +22,12 @@ proc checkPartialConstructedType(info: TLineInfo, t: PType) =
proc checkConstructedType*(info: TLineInfo, typ: PType) = proc checkConstructedType*(info: TLineInfo, typ: PType) =
var t = typ.skipTypes({tyDistinct}) var t = typ.skipTypes({tyDistinct})
if t.kind in {tyTypeClass}: nil if t.kind in tyTypeClasses: nil
elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
localError(info, errInvalidPragmaX, "acyclic") localError(info, errInvalidPragmaX, "acyclic")
elif t.kind == tyVar and t.sons[0].kind == tyVar: elif t.kind == tyVar and t.sons[0].kind == tyVar:
localError(info, errVarVarTypeNotAllowed) localError(info, errVarVarTypeNotAllowed)
elif computeSize(t) < 0: elif computeSize(t) == szIllegalRecursion:
localError(info, errIllegalRecursionInTypeX, typeToString(t)) localError(info, errIllegalRecursionInTypeX, typeToString(t))
when false: when false:
if t.kind == tyObject and t.sons[0] != nil: if t.kind == tyObject and t.sons[0] != nil:
@ -50,7 +53,8 @@ proc searchInstTypes*(key: PType): PType =
block matchType: block matchType:
for j in 1 .. high(key.sons): for j in 1 .. high(key.sons):
# XXX sameType is not really correct for nested generics? # XXX sameType is not really correct for nested generics?
if not sameType(inst.sons[j], key.sons[j]): if not compareTypes(inst.sons[j], key.sons[j],
flags = {ExactGenericParams}):
break matchType break matchType
return inst return inst
@ -66,25 +70,84 @@ type
c*: PContext c*: PContext
typeMap*: TIdTable # map PType to PType typeMap*: TIdTable # map PType to PType
symMap*: TIdTable # map PSym to PSym symMap*: TIdTable # map PSym to PSym
localCache*: TIdTable # local cache for remembering alraedy replaced
# types during instantiation of meta types
# (they are not stored in the global cache)
info*: TLineInfo info*: TLineInfo
allowMetaTypes*: bool # allow types such as seq[Number]
# i.e. the result contains unresolved generics
proc replaceTypeVarsT*(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) =
when false:
if t != nil and tfHasMeta in t.flags and
cl.allowMetaTypes == false:
echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1)
debug t
writeStackTrace()
quit 1
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
result = replaceTypeVarsTAux(cl, t)
checkMetaInvariants(cl, result)
proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
result = copyNode(n) result = copyNode(n)
result.typ = replaceTypeVarsT(cl, n.typ) result.typ = replaceTypeVarsT(cl, n.typ)
if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym) if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym)
for i in 0 .. safeLen(n)-1: let isCall = result.kind in nkCallKinds
for i in 0 .. <n.safeLen:
# XXX HACK: ``f(a, b)``, avoid to instantiate `f` # XXX HACK: ``f(a, b)``, avoid to instantiate `f`
if i == 0: result.add(n[i]) if isCall and i == 0: result.add(n[i])
else: result.add(prepareNode(cl, n[i])) else: result.add(prepareNode(cl, n[i]))
proc isTypeParam(n: PNode): bool =
# XXX: generic params should use skGenericParam instead of skType
return n.kind == nkSym and
(n.sym.kind == skGenericParam or
(n.sym.kind == skType and sfFromGeneric in n.sym.flags))
proc hasGenericArguments*(n: PNode): bool =
if n.kind == nkSym:
return n.sym.kind == skGenericParam or
(n.sym.kind == skType and
n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
else:
for s in n.sons:
if hasGenericArguments(s): return true
return false
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
# This is needed fo tgenericshardcases
# It's possible that a generic param will be used in a proc call to a
# typedesc accepting proc. After generic param substitution, such procs
# should be optionally instantiated with the correct type. In order to
# perform this instantiation, we need to re-run the generateInstance path
# in the compiler, but it's quite complicated to do so at the moment so we
# resort to a mild hack; the head symbol of the call is temporary reset and
# overload resolution is executed again (which may trigger generateInstance).
if n.kind in nkCallKinds and sfFromGeneric in n[0].sym.flags:
var needsFixing = false
for i in 1 .. <n.safeLen:
if isTypeParam(n[i]): needsFixing = true
if needsFixing:
n.sons[0] = newSymNode(n.sons[0].sym.owner)
return cl.c.semOverloadedCall(cl.c, n, n, {skProc})
for i in 0 .. <n.safeLen:
n.sons[i] = reResolveCallsWithTypedescParams(cl, n[i])
return n
proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode = proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
if n == nil: return if n == nil: return
result = copyNode(n) result = copyNode(n)
if n.typ != nil:
result.typ = replaceTypeVarsT(cl, n.typ) result.typ = replaceTypeVarsT(cl, n.typ)
checkMetaInvariants(cl, result.typ)
case n.kind case n.kind
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit: of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
discard discard
@ -111,6 +174,10 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
result = replaceTypeVarsN(cl, branch) result = replaceTypeVarsN(cl, branch)
else: else:
result = newNodeI(nkRecList, n.info) result = newNodeI(nkRecList, n.info)
of nkStaticExpr:
var n = prepareNode(cl, n)
n = reResolveCallsWithTypedescParams(cl, n)
result = cl.c.semExpr(cl.c, n)
else: else:
var length = sonsLen(n) var length = sonsLen(n)
if length > 0: if length > 0:
@ -132,11 +199,18 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType = proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
result = PType(idTableGet(cl.typeMap, t)) result = PType(idTableGet(cl.typeMap, t))
if result == nil: if result == nil:
if cl.allowMetaTypes or tfRetType in t.flags: return
localError(t.sym.info, errCannotInstantiateX, typeToString(t)) localError(t.sym.info, errCannotInstantiateX, typeToString(t))
result = errorType(cl.c) result = errorType(cl.c)
elif result.kind == tyGenericParam: 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 =
# XXX: relying on allowMetaTypes is a kludge
result = copyType(t, t.owner, cl.allowMetaTypes)
result.flags.incl tfFromGeneric
result.flags.excl tfInstClearedFlags
proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
# tyGenericInvokation[A, tyGenericInvokation[A, B]] # tyGenericInvokation[A, tyGenericInvokation[A, B]]
# is difficult to handle: # is difficult to handle:
@ -144,29 +218,37 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
if body.kind != tyGenericBody: internalError(cl.info, "no generic body") if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
var header: PType = nil var header: PType = nil
# search for some instantiation here: # search for some instantiation here:
if cl.allowMetaTypes:
result = PType(idTableGet(cl.localCache, t))
else:
result = searchInstTypes(t) result = searchInstTypes(t)
if result != nil: return if result != nil: return
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
var x = t.sons[i] var x = t.sons[i]
if x.kind == tyGenericParam: if x.kind == tyGenericParam:
x = lookupTypeVar(cl, x) x = lookupTypeVar(cl, x)
if header == nil: header = copyType(t, t.owner, false) if x != nil:
if header == nil: header = instCopyType(cl, t)
header.sons[i] = x header.sons[i] = x
propagateToOwner(header, x) propagateToOwner(header, x)
#idTablePut(cl.typeMap, body.sons[i-1], x)
if header != nil: if header != nil:
# search again after first pass: # search again after first pass:
result = searchInstTypes(header) result = searchInstTypes(header)
if result != nil: return if result != nil: return
else: else:
header = copyType(t, t.owner, false) header = instCopyType(cl, t)
result = newType(tyGenericInst, t.sons[0].owner)
# be careful not to propagate unnecessary flags here (don't use rawAddSon)
result.sons = @[header.sons[0]]
# ugh need another pass for deeply recursive generic types (e.g. PActor) # ugh need another pass for deeply recursive generic types (e.g. PActor)
# we need to add the candidate here, before it's fully instantiated for # we need to add the candidate here, before it's fully instantiated for
# recursive instantions: # recursive instantions:
result = newType(tyGenericInst, t.sons[0].owner) if not cl.allowMetaTypes:
result.rawAddSon(header.sons[0])
cacheTypeInst(result) cacheTypeInst(result)
else:
idTablePut(cl.localCache, t, result)
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
var x = replaceTypeVarsT(cl, t.sons[i]) var x = replaceTypeVarsT(cl, t.sons[i])
@ -181,10 +263,9 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
rawAddSon(result, header.sons[i]) rawAddSon(result, header.sons[i])
var newbody = replaceTypeVarsT(cl, lastSon(body)) var newbody = replaceTypeVarsT(cl, lastSon(body))
newbody.flags = newbody.flags + t.flags + body.flags newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
result.flags = result.flags + newbody.flags result.flags = result.flags + newbody.flags
newbody.callConv = body.callConv newbody.callConv = body.callConv
newbody.n = replaceTypeVarsN(cl, lastSon(body).n)
# This type may be a generic alias and we want to resolve it here. # This type may be a generic alias and we want to resolve it here.
# One step is enough, because the recursive nature of # One step is enough, because the recursive nature of
# handleGenericInvokation will handle the alias-to-alias-to-alias case # handleGenericInvokation will handle the alias-to-alias-to-alias case
@ -192,53 +273,149 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
rawAddSon(result, newbody) rawAddSon(result, newbody)
checkPartialConstructedType(cl.info, newbody) checkPartialConstructedType(cl.info, newbody)
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = proc eraseVoidParams(t: PType) =
if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
t.sons[0] = nil
for i in 1 .. <t.sonsLen:
# don't touch any memory unless necessary
if t.sons[i].kind == tyEmpty:
var pos = i
for j in i+1 .. <t.sonsLen:
if t.sons[j].kind != tyEmpty:
t.sons[pos] = t.sons[j]
t.n.sons[pos] = t.n.sons[j]
inc pos
setLen t.sons, pos
setLen t.n.sons, pos
return
proc skipIntLiteralParams(t: PType) =
for i in 0 .. <t.sonsLen:
let p = t.sons[i]
if p == nil: continue
let skipped = p.skipIntLit
if skipped != p:
t.sons[i] = skipped
if i > 0: t.n.sons[i].sym.typ = skipped
proc propagateFieldFlags(t: PType, n: PNode) =
# This is meant for objects and tuples
# The type must be fully instantiated!
internalAssert n.kind != nkRecWhen
case n.kind
of nkSym:
propagateToOwner(t, n.sym.typ)
of nkRecList, nkRecCase, nkOfBranch, nkElse:
if n.sons != nil:
for son in n.sons:
propagateFieldFlags(t, son)
else: discard
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
result = t result = t
if t == nil: return if t == nil: return
if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses:
let lookup = PType(idTableGet(cl.typeMap, t))
if lookup != nil: return lookup
case t.kind case t.kind
of tyTypeClass: discard
of tyGenericParam:
result = lookupTypeVar(cl, t)
if result.kind == tyGenericInvokation:
result = handleGenericInvokation(cl, result)
of tyExpr:
if t.sym != nil and t.sym.kind == skGenericParam:
result = lookupTypeVar(cl, t)
of tyGenericInvokation: of tyGenericInvokation:
result = handleGenericInvokation(cl, t) result = handleGenericInvokation(cl, t)
of tyGenericBody: of tyGenericBody:
internalError(cl.info, "ReplaceTypeVarsT: tyGenericBody") internalError(cl.info, "ReplaceTypeVarsT: tyGenericBody" )
result = replaceTypeVarsT(cl, lastSon(t)) result = replaceTypeVarsT(cl, lastSon(t))
of tyFromExpr:
var n = prepareNode(cl, t.n)
n = cl.c.semConstExpr(cl.c, n)
if n.typ.kind == tyTypeDesc:
# XXX: sometimes, chained typedescs enter here.
# It may be worth investigating why this is happening,
# because it may cause other bugs elsewhere.
result = n.typ.skipTypes({tyTypeDesc})
# result = n.typ.base
else:
if n.typ.kind != tyStatic:
# XXX: In the future, semConstExpr should
# return tyStatic values to let anyone make
# use of this knowledge. The patching here
# won't be necessary then.
result = newTypeS(tyStatic, cl.c)
result.sons = @[n.typ]
result.n = n
else:
result = n.typ
of tyInt: of tyInt:
result = skipIntLit(t) result = skipIntLit(t)
# XXX now there are also float literals # XXX now there are also float literals
else:
if t.kind == tyArray:
let idxt = t.sons[0]
if idxt.kind == tyExpr and
idxt.sym != nil and idxt.sym.kind == skGenericParam:
let value = lookupTypeVar(cl, idxt).n
t.sons[0] = makeRangeType(cl.c, 0, value.intVal - 1, value.info)
if containsGenericType(t):
result = copyType(t, t.owner, false)
incl(result.flags, tfFromGeneric)
result.size = -1 # needs to be recomputed
for i in countup(0, sonsLen(result) - 1):
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
result.n = replaceTypeVarsN(cl, result.n)
if result.kind in GenericTypes:
localError(cl.info, errCannotInstantiateX, typeToString(t, preferName))
if result.kind == tyProc and result.sons[0] != nil:
if result.sons[0].kind == tyEmpty:
result.sons[0] = nil
proc generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode, of tyTypeDesc:
let lookup = PType(idTableGet(cl.typeMap, t)) # lookupTypeVar(cl, t)
if lookup != nil:
result = lookup
if tfUnresolved in t.flags: result = result.base
elif t.sonsLen > 0:
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
of tyGenericInst:
result = instCopyType(cl, t)
for i in 1 .. <result.sonsLen:
result.sons[i] = ReplaceTypeVarsT(cl, result.sons[i])
propagateToOwner(result, result.lastSon)
else:
if containsGenericType(t):
result = instCopyType(cl, t)
result.size = -1 # needs to be recomputed
for i in countup(0, sonsLen(result) - 1):
if result.sons[i] != nil:
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
propagateToOwner(result, result.sons[i])
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
of tyArray:
let idx = result.sons[0]
if 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:
propagateFieldFlags(result, result.n)
of tyProc:
eraseVoidParams(result)
skipIntLiteralParams(result)
else: discard
proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
t: PType): PType = t: PType): PType =
var cl: TReplTypeVars var cl: TReplTypeVars
initIdTable(cl.symMap) initIdTable(cl.symMap)
copyIdTable(cl.typeMap, pt) copyIdTable(cl.typeMap, pt)
cl.info = arg.info initIdTable(cl.localCache)
cl.info = info
cl.c = p cl.c = p
pushInfoContext(arg.info) pushInfoContext(info)
result = replaceTypeVarsT(cl, t) result = replaceTypeVarsT(cl, t)
popInfoContext() popInfoContext()
template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode,
t: PType): expr =
generateTypeInstance(p, pt, arg.info, t)

View file

@ -22,6 +22,7 @@ type
csEmpty, csMatch, csNoMatch csEmpty, csMatch, csNoMatch
TCandidate* {.final.} = object TCandidate* {.final.} = object
c*: PContext
exactMatches*: int # also misused to prefer iters over procs exactMatches*: int # also misused to prefer iters over procs
genericMatches: int # also misused to prefer constraints genericMatches: int # also misused to prefer constraints
subtypeMatches: int subtypeMatches: int
@ -30,7 +31,8 @@ type
state*: TCandidateState state*: TCandidateState
callee*: PType # may not be nil! callee*: PType # may not be nil!
calleeSym*: PSym # may be nil calleeSym*: PSym # may be nil
calleeScope: int # may be -1 for unknown scope calleeScope*: int # scope depth:
# is this a top-level symbol or a nested proc?
call*: PNode # modified call call*: PNode # modified call
bindings*: TIdTable # maps types to types bindings*: TIdTable # maps types to types
baseTypeMatch: bool # needed for conversions from T to openarray[T] baseTypeMatch: bool # needed for conversions from T to openarray[T]
@ -58,7 +60,9 @@ const
proc markUsed*(n: PNode, s: PSym) proc markUsed*(n: PNode, s: PSym)
proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} = proc initCandidateAux(ctx: PContext,
c: var TCandidate, callee: PType) {.inline.} =
c.c = ctx
c.exactMatches = 0 c.exactMatches = 0
c.subtypeMatches = 0 c.subtypeMatches = 0
c.convMatches = 0 c.convMatches = 0
@ -71,18 +75,26 @@ proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} =
c.genericConverter = false c.genericConverter = false
c.inheritancePenalty = 0 c.inheritancePenalty = 0
proc initCandidate*(c: var TCandidate, callee: PType) = proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) =
initCandidateAux(c, callee) initCandidateAux(ctx, c, callee)
c.calleeSym = nil c.calleeSym = nil
initIdTable(c.bindings) initIdTable(c.bindings)
proc put(t: var TIdTable, key, val: PType) {.inline.} = proc put(t: var TIdTable, key, val: PType) {.inline.} =
idTablePut(t, key, val) idTablePut(t, key, val)
proc initCandidate*(c: var TCandidate, callee: PSym, binding: PNode, proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
calleeScope = -1) = binding: PNode, calleeScope = -1) =
initCandidateAux(c, callee.typ) initCandidateAux(ctx, c, callee.typ)
c.calleeSym = callee c.calleeSym = callee
if callee.kind in skProcKinds and calleeScope == -1:
if callee.originatingModule == ctx.module:
let rootSym = if sfFromGeneric notin callee.flags: callee
else: callee.owner
c.calleeScope = rootSym.scope.depthLevel
else:
c.calleeScope = 1
else:
c.calleeScope = calleeScope c.calleeScope = calleeScope
initIdTable(c.bindings) initIdTable(c.bindings)
c.errors = nil c.errors = nil
@ -90,13 +102,17 @@ proc initCandidate*(c: var TCandidate, callee: PSym, binding: PNode,
var typeParams = callee.ast[genericParamsPos] var typeParams = callee.ast[genericParamsPos]
for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1): for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
var formalTypeParam = typeParams.sons[i-1].typ var formalTypeParam = typeParams.sons[i-1].typ
#debug(formalTypeParam) var bound = binding[i].typ
put(c.bindings, formalTypeParam, binding[i].typ) if formalTypeParam.kind != tyTypeDesc:
bound = bound.skipTypes({tyTypeDesc})
put(c.bindings, formalTypeParam, bound)
proc newCandidate*(callee: PSym, binding: PNode, calleeScope = -1): TCandidate = proc newCandidate*(ctx: PContext, callee: PSym,
initCandidate(result, callee, binding, calleeScope) binding: PNode, calleeScope = -1): TCandidate =
initCandidate(ctx, result, callee, binding, calleeScope)
proc copyCandidate(a: var TCandidate, b: TCandidate) = proc copyCandidate(a: var TCandidate, b: TCandidate) =
a.c = b.c
a.exactMatches = b.exactMatches a.exactMatches = b.exactMatches
a.subtypeMatches = b.subtypeMatches a.subtypeMatches = b.subtypeMatches
a.convMatches = b.convMatches a.convMatches = b.convMatches
@ -124,7 +140,7 @@ proc sumGeneric(t: PType): int =
result = ord(t.kind == tyGenericInvokation) result = ord(t.kind == tyGenericInvokation)
for i in 0 .. <t.len: result += t.sons[i].sumGeneric for i in 0 .. <t.len: result += t.sons[i].sumGeneric
break break
of tyGenericParam, tyExpr, tyStmt, tyTypeDesc, tyTypeClass: break of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc, tyTypeClass: break
else: return 0 else: return 0
proc complexDisambiguation(a, b: PType): int = proc complexDisambiguation(a, b: PType): int =
@ -159,7 +175,6 @@ proc cmpCandidates*(a, b: TCandidate): int =
if result != 0: return if result != 0: return
result = a.convMatches - b.convMatches result = a.convMatches - b.convMatches
if result != 0: return if result != 0: return
if (a.calleeScope != -1) and (b.calleeScope != -1):
result = a.calleeScope - b.calleeScope result = a.calleeScope - b.calleeScope
if result != 0: return if result != 0: return
# the other way round because of other semantics: # the other way round because of other semantics:
@ -203,7 +218,7 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1): string =
add(result, argTypeToString(arg)) add(result, argTypeToString(arg))
if i != sonsLen(n) - 1: add(result, ", ") if i != sonsLen(n) - 1: add(result, ", ")
proc typeRel*(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation proc typeRel*(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation
proc concreteType(c: TCandidate, t: PType): PType = proc concreteType(c: TCandidate, t: PType): PType =
case t.kind case t.kind
of tyArrayConstr: of tyArrayConstr:
@ -296,24 +311,27 @@ proc minRel(a, b: TTypeRelation): TTypeRelation =
if a <= b: result = a if a <= b: result = a
else: result = b else: result = b
proc tupleRel(c: var TCandidate, f, a: PType): TTypeRelation = proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = isNone result = isNone
if sameType(f, a): if sameType(f, a): result = isEqual
result = isEqual
elif sonsLen(a) == sonsLen(f): elif sonsLen(a) == sonsLen(f):
result = isEqual result = isEqual
for i in countup(0, sonsLen(f) - 1): let firstField = if f.kind == tyTuple: 0
else: 1
for i in countup(firstField, sonsLen(f) - 1):
var m = typeRel(c, f.sons[i], a.sons[i]) var m = typeRel(c, f.sons[i], a.sons[i])
if m < isSubtype: return isNone if m < isSubtype: return isNone
result = minRel(result, m) result = minRel(result, m)
if f.n != nil and a.n != nil: if f.n != nil and a.n != nil:
for i in countup(0, sonsLen(f.n) - 1): for i in countup(0, sonsLen(f.n) - 1):
# check field names: # check field names:
if f.n.sons[i].kind != nkSym: internalError(f.n.info, "tupleRel") if f.n.sons[i].kind != nkSym: internalError(f.n.info, "recordRel")
elif a.n.sons[i].kind != nkSym: internalError(a.n.info, "tupleRel") elif a.n.sons[i].kind != nkSym: internalError(a.n.info, "recordRel")
else: else:
var x = f.n.sons[i].sym var x = f.n.sons[i].sym
var y = a.n.sons[i].sym var y = a.n.sons[i].sym
if f.kind == tyObject and typeRel(c, x.typ, y.typ) < isSubtype:
return isNone
if x.name.id != y.name.id: return isNone if x.name.id != y.name.id: return isNone
proc allowsNil(f: PType): TTypeRelation {.inline.} = proc allowsNil(f: PType): TTypeRelation {.inline.} =
@ -365,10 +383,6 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
of tyNil: result = f.allowsNil of tyNil: result = f.allowsNil
else: nil else: nil
proc matchTypeClass(c: var TCandidate, f, a: PType): TTypeRelation =
result = if matchTypeClass(c.bindings, f, a): isGeneric
else: isNone
proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
let let
a0 = firstOrd(a) a0 = firstOrd(a)
@ -385,7 +399,7 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
else: else:
result = isNone result = isNone
proc typeRel(c: var TCandidate, f, a: 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:
# #
# 1) When used with concrete types, it will check for type equivalence # 1) When used with concrete types, it will check for type equivalence
@ -404,19 +418,28 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
result = isNone result = isNone
assert(f != nil) assert(f != nil)
assert(a != nil) assert(aOrig != nil)
# var and static arguments match regular modifier-free types
let a = aOrig.skipTypes({tyStatic, tyVar})
if a.kind == tyGenericInst and if a.kind == tyGenericInst and
skipTypes(f, {tyVar}).kind notin { skipTypes(f, {tyVar}).kind notin {
tyGenericBody, tyGenericInvokation, tyGenericBody, tyGenericInvokation,
tyGenericParam, tyTypeClass}: tyGenericInst, tyGenericParam} + tyTypeClasses:
return typeRel(c, f, lastSon(a)) return typeRel(c, f, lastSon(a))
if a.kind == tyVar and f.kind != tyVar:
return typeRel(c, f, a.sons[0])
template bindingRet(res) = template bindingRet(res) =
when res == isGeneric: put(c.bindings, f, a) when res == isGeneric:
let bound = aOrig.skipTypes({tyRange}).skipIntLit
put(c.bindings, f, bound)
return res return res
template considerPreviousT(body: stmt) {.immediate.} =
var prev = PType(idTableGet(c.bindings, f))
if prev == nil: body
else: return typeRel(c, prev, a)
case a.kind case a.kind
of tyOr: of tyOr:
# seq[int|string] vs seq[number] # seq[int|string] vs seq[number]
@ -489,8 +512,8 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
of tyFloat64: result = handleFloatRange(f, a) of tyFloat64: result = handleFloatRange(f, a)
of tyFloat128: result = handleFloatRange(f, a) of tyFloat128: result = handleFloatRange(f, a)
of tyVar: of tyVar:
if a.kind == f.kind: result = typeRel(c, base(f), base(a)) if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base)
else: result = typeRel(c, base(f), a) else: result = typeRel(c, f.base, aOrig)
of tyArray, tyArrayConstr: of tyArray, tyArrayConstr:
# tyArrayConstr cannot happen really, but # tyArrayConstr cannot happen really, but
# we wanna be safe here # we wanna be safe here
@ -544,7 +567,6 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
of tyOrdinal: of tyOrdinal:
if isOrdinalType(a): if isOrdinalType(a):
var x = if a.kind == tyOrdinal: a.sons[0] else: a var x = if a.kind == tyOrdinal: a.sons[0] else: a
if f.sonsLen == 0: if f.sonsLen == 0:
result = isGeneric result = isGeneric
else: else:
@ -556,10 +578,12 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
of tyNil: of tyNil:
if a.kind == f.kind: result = isEqual if a.kind == f.kind: result = isEqual
of tyTuple: of tyTuple:
if a.kind == tyTuple: result = tupleRel(c, f, a) if a.kind == tyTuple: result = recordRel(c, f, a)
of tyObject: of tyObject:
if a.kind == tyObject: if a.kind == tyObject:
if sameObjectTypes(f, a): result = isEqual if sameObjectTypes(f, a):
result = isEqual
# elif tfHasMeta in f.flags: result = recordRel(c, f, a)
else: else:
var depth = isObjectSubtype(a, f) var depth = isObjectSubtype(a, f)
if depth > 0: if depth > 0:
@ -641,9 +665,24 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
if a.kind == tyEmpty: result = isEqual if a.kind == tyEmpty: result = isEqual
of tyGenericInst: of tyGenericInst:
let roota = a.skipGenericAlias
let rootf = f.skipGenericAlias
if a.kind == tyGenericInst and roota.base == rootf.base :
for i in 1 .. rootf.sonsLen-2:
let ff = rootf.sons[i]
let aa = roota.sons[i]
result = typeRel(c, ff, aa)
if result == isNone: return
if ff.kind == tyRange and result != isEqual: return isNone
result = isGeneric
else:
result = typeRel(c, lastSon(f), a) result = typeRel(c, lastSon(f), a)
of tyGenericBody: of tyGenericBody:
considerPreviousT:
if a.kind == tyGenericInst and a.sons[0] == f:
bindingRet isGeneric
let ff = lastSon(f) let ff = lastSon(f)
if ff != nil: result = typeRel(c, ff, a) if ff != nil: result = typeRel(c, ff, a)
@ -672,35 +711,53 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
put(c.bindings, f.sons[i], x) put(c.bindings, f.sons[i], x)
of tyAnd: of tyAnd:
considerPreviousT:
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, a) == isNone: if typeRel(c, branch, aOrig) == isNone:
return isNone return isNone
bindingRet isGeneric bindingRet isGeneric
of tyOr: of tyOr:
considerPreviousT:
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, a) != isNone: if typeRel(c, branch, aOrig) != isNone:
bindingRet isGeneric bindingRet isGeneric
return isNone return isNone
of tyNot: of tyNot:
considerPreviousT:
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, a) != isNone: if typeRel(c, branch, aOrig) != isNone:
return isNone return isNone
bindingRet isGeneric bindingRet isGeneric
of tyAnything: of tyAnything:
var prev = PType(idTableGet(c.bindings, f)) considerPreviousT:
if prev == nil:
var concrete = concreteType(c, a) var concrete = concreteType(c, a)
if concrete != nil and doBind: if concrete != nil and doBind:
put(c.bindings, f, concrete) put(c.bindings, f, concrete)
return isGeneric return isGeneric
of tyBuiltInTypeClass:
considerPreviousT:
let targetKind = f.sons[0].kind
if targetKind == a.skipTypes({tyRange, tyGenericInst}).kind or
(targetKind in {tyProc, tyPointer} and a.kind == tyNil):
put(c.bindings, f, a)
return isGeneric
else: else:
return typeRel(c, prev, a) return isNone
of tyCompositeTypeClass:
considerPreviousT:
if typeRel(c, f.sons[1], a) != isNone:
put(c.bindings, f, a)
return isGeneric
else:
return isNone
of tyGenericParam, tyTypeClass: of tyGenericParam, tyTypeClass:
var x = PType(idTableGet(c.bindings, f)) var x = PType(idTableGet(c.bindings, f))
@ -722,10 +779,10 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
else: else:
result = isNone result = isNone
else: else:
if a.kind == tyTypeClass: if f.sonsLen > 0:
result = isGeneric result = typeRel(c, f.lastSon, a)
else: else:
result = matchTypeClass(c, f, a) result = isGeneric
if result == isGeneric: if result == isGeneric:
var concrete = concreteType(c, a) var concrete = concreteType(c, a)
@ -739,6 +796,14 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, x, a) # check if it fits result = typeRel(c, x, a) # check if it fits
of tyStatic:
if aOrig.kind == tyStatic:
result = typeRel(c, f.lastSon, a)
if result != isNone: put(c.bindings, f, aOrig)
else:
result = isNone
of tyTypeDesc: of tyTypeDesc:
var prev = PType(idTableGet(c.bindings, f)) var prev = PType(idTableGet(c.bindings, f))
if prev == nil: if prev == nil:
@ -746,8 +811,8 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
if f.sonsLen == 0: if f.sonsLen == 0:
result = isGeneric result = isGeneric
else: else:
result = matchTypeClass(c, f, a.sons[0]) result = typeRel(c, f.sons[0], a.sons[0])
if result == isGeneric: if result != isNone:
put(c.bindings, f, a) put(c.bindings, f, a)
else: else:
result = isNone result = isNone
@ -756,16 +821,19 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
let toMatch = if tfUnresolved in f.flags: a let toMatch = if tfUnresolved in f.flags: a
else: a.sons[0] else: a.sons[0]
result = typeRel(c, prev.sons[0], toMatch) result = typeRel(c, prev.sons[0], toMatch)
of tyExpr, tyStmt: of tyExpr, tyStmt:
result = isGeneric result = isGeneric
of tyProxy: of tyProxy:
result = isEqual result = isEqual
else: internalError("typeRel: " & $f.kind) else: internalError("typeRel: " & $f.kind)
proc cmpTypes*(f, a: PType): TTypeRelation = proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
var c: TCandidate var m: TCandidate
initCandidate(c, f) initCandidate(c, m, f)
result = typeRel(c, f, a) result = typeRel(m, f, a)
proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate, proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate,
f: PType): PType = f: PType): PType =
@ -887,47 +955,34 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
result = arg result = arg
put(m.bindings, f, a) put(m.bindings, f, a)
proc paramTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType, proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
argSemantized, argOrig: PNode): PNode = argSemantized, argOrig: PNode): PNode =
var var
r: TTypeRelation fMaybeStatic = f.skipTypes({tyDistinct})
arg = argSemantized arg = argSemantized
argType = argType
c = m.c
let if tfHasStatic in fMaybeStatic.flags:
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc}) # XXX: When implicit statics are the default
else: argType # this will be done earlier - we just have to
fMaybeExpr = f.skipTypes({tyDistinct}) # make sure that static types enter here
var evaluated = c.semTryConstExpr(c, arg)
case fMaybeExpr.kind
of tyExpr:
if fMaybeExpr.sonsLen == 0:
r = isGeneric
else:
if a.kind == tyExpr:
internalAssert a.len > 0
r = typeRel(m, f.lastSon, a.lastSon)
else:
let match = matchTypeClass(m.bindings, fMaybeExpr, a)
if not match: r = isNone
else:
# XXX: Ideally, this should happen much earlier somewhere near
# semOpAux, but to do that, we need to be able to query the
# overload set to determine whether compile-time value is expected
# for the param before entering the full-blown sigmatch algorithm.
# This is related to the immediate pragma since querying the
# overload set could help there too.
var evaluated = c.semConstExpr(c, arg)
if evaluated != nil: if evaluated != nil:
r = isGeneric arg.typ = newTypeS(tyStatic, c)
arg.typ = newTypeS(tyExpr, c)
arg.typ.sons = @[evaluated.typ] arg.typ.sons = @[evaluated.typ]
arg.typ.n = evaluated arg.typ.n = evaluated
argType = arg.typ
if r == isGeneric: var
put(m.bindings, f, arg.typ) r: TTypeRelation
a = if c.InTypeClass > 0: argType.skipTypes({tyTypeDesc})
else: argType
case fMaybeStatic.kind
of tyTypeClass, tyParametricTypeClass: of tyTypeClass, tyParametricTypeClass:
if fMaybeExpr.n != nil: if fMaybeStatic.n != nil:
let match = matchUserTypeClass(c, m, arg, fMaybeExpr, a) let match = matchUserTypeClass(c, m, arg, fMaybeStatic, a)
if match != nil: if match != nil:
r = isGeneric r = isGeneric
arg = match arg = match
@ -935,6 +990,9 @@ proc paramTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType,
r = isNone r = isNone
else: else:
r = typeRel(m, f, a) r = typeRel(m, f, a)
of tyExpr:
r = isGeneric
put(m.bindings, f, arg.typ)
else: else:
r = typeRel(m, f, a) r = typeRel(m, f, a)
@ -961,6 +1019,8 @@ proc paramTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType,
result = argOrig[bodyPos] result = argOrig[bodyPos]
elif f.kind == tyTypeDesc: elif f.kind == tyTypeDesc:
result = arg result = arg
elif f.kind == tyStatic:
result = arg.typ.n
else: else:
result = argOrig result = argOrig
else: else:
@ -1003,19 +1063,20 @@ proc paramTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType,
else: else:
result = userConvMatch(c, m, base(f), a, arg) result = userConvMatch(c, m, base(f), a, arg)
proc paramTypesMatch*(c: PContext, m: var TCandidate, f, a: PType, proc paramTypesMatch*(m: var TCandidate, f, a: PType,
arg, argOrig: PNode): PNode = arg, argOrig: PNode): PNode =
if arg == nil or arg.kind notin nkSymChoices: if arg == nil or arg.kind notin nkSymChoices:
result = paramTypesMatchAux(c, m, f, a, arg, argOrig) result = paramTypesMatchAux(m, f, a, arg, argOrig)
else: else:
# CAUTION: The order depends on the used hashing scheme. Thus it is # CAUTION: The order depends on the used hashing scheme. Thus it is
# incorrect to simply use the first fitting match. However, to implement # incorrect to simply use the first fitting match. However, to implement
# this correctly is inefficient. We have to copy `m` here to be able to # this correctly is inefficient. We have to copy `m` here to be able to
# roll back the side effects of the unification algorithm. # roll back the side effects of the unification algorithm.
let c = m.c
var x, y, z: TCandidate var x, y, z: TCandidate
initCandidate(x, m.callee) initCandidate(c, x, m.callee)
initCandidate(y, m.callee) initCandidate(c, y, m.callee)
initCandidate(z, m.callee) initCandidate(c, z, m.callee)
x.calleeSym = m.calleeSym x.calleeSym = m.calleeSym
y.calleeSym = m.calleeSym y.calleeSym = m.calleeSym
z.calleeSym = m.calleeSym z.calleeSym = m.calleeSym
@ -1047,7 +1108,7 @@ proc paramTypesMatch*(c: PContext, m: var TCandidate, f, a: PType,
else: else:
# only one valid interpretation found: # only one valid interpretation found:
markUsed(arg, arg.sons[best].sym) markUsed(arg, arg.sons[best].sym)
result = paramTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best], result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best],
argOrig) argOrig)
proc setSon(father: PNode, at: int, son: PNode) = proc setSon(father: PNode, at: int, son: PNode) =
@ -1138,7 +1199,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
m.baseTypeMatch = false m.baseTypeMatch = false
n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1]) n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1])
n.sons[a].typ = n.sons[a].sons[1].typ n.sons[a].typ = n.sons[a].sons[1].typ
var arg = paramTypesMatch(c, m, formal.typ, n.sons[a].typ, var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
n.sons[a].sons[1], nOrig.sons[a].sons[1]) n.sons[a].sons[1], nOrig.sons[a].sons[1])
if arg == nil: if arg == nil:
m.state = csNoMatch m.state = csNoMatch
@ -1168,7 +1229,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
elif formal != nil: elif formal != nil:
m.baseTypeMatch = false m.baseTypeMatch = false
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = paramTypesMatch(c, m, formal.typ, n.sons[a].typ, var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
n.sons[a], nOrig.sons[a]) n.sons[a], nOrig.sons[a])
if (arg != nil) and m.baseTypeMatch and (container != nil): if (arg != nil) and m.baseTypeMatch and (container != nil):
addSon(container, arg) addSon(container, arg)
@ -1191,7 +1252,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
return return
m.baseTypeMatch = false m.baseTypeMatch = false
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = paramTypesMatch(c, m, formal.typ, n.sons[a].typ, var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
n.sons[a], nOrig.sons[a]) n.sons[a], nOrig.sons[a])
if arg == nil: if arg == nil:
m.state = csNoMatch m.state = csNoMatch
@ -1245,8 +1306,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
proc argtypeMatches*(c: PContext, f, a: PType): bool = proc argtypeMatches*(c: PContext, f, a: PType): bool =
var m: TCandidate var m: TCandidate
initCandidate(m, f) initCandidate(c, m, f)
let res = paramTypesMatch(c, m, f, a, ast.emptyNode, nil) let res = paramTypesMatch(m, f, a, ast.emptyNode, nil)
#instantiateGenericConverters(c, res, m) #instantiateGenericConverters(c, res, m)
# XXX this is used by patterns.nim too; I think it's better to not # XXX this is used by patterns.nim too; I think it's better to not
# instantiate generic converters for that # instantiate generic converters for that
@ -1308,7 +1369,7 @@ tests:
setup: setup:
var c: TCandidate var c: TCandidate
InitCandidate(c, nil) InitCandidate(nil, c, nil)
template yes(x, y) = template yes(x, y) =
test astToStr(x) & " is " & astToStr(y): test astToStr(x) & " is " & astToStr(y):

View file

@ -119,7 +119,7 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
case candidate.kind case candidate.kind
of OverloadableSyms: of OverloadableSyms:
var m: TCandidate var m: TCandidate
initCandidate(m, candidate, nil) initCandidate(c, m, candidate, nil)
sigmatch.partialMatch(c, n, nOrig, m) sigmatch.partialMatch(c, n, nOrig, m)
result = m.state != csNoMatch result = m.state != csNoMatch
else: else:

View file

@ -10,7 +10,7 @@
# this module contains routines for accessing and iterating over types # this module contains routines for accessing and iterating over types
import import
intsets, ast, astalgo, trees, msgs, strutils, platform intsets, ast, astalgo, trees, msgs, strutils, platform, renderer
proc firstOrd*(t: PType): BiggestInt proc firstOrd*(t: PType): BiggestInt
proc lastOrd*(t: PType): BiggestInt proc lastOrd*(t: PType): BiggestInt
@ -64,7 +64,6 @@ const
typedescPtrs* = abstractPtrs + {tyTypeDesc} typedescPtrs* = abstractPtrs + {tyTypeDesc}
typedescInst* = abstractInst + {tyTypeDesc} typedescInst* = abstractInst + {tyTypeDesc}
proc skipTypes*(t: PType, kinds: TTypeKinds): PType
proc containsObject*(t: PType): bool proc containsObject*(t: PType): bool
proc containsGarbageCollectedRef*(typ: PType): bool proc containsGarbageCollectedRef*(typ: PType): bool
proc containsHiddenPointer*(typ: PType): bool proc containsHiddenPointer*(typ: PType): bool
@ -148,10 +147,6 @@ proc skipGeneric(t: PType): PType =
result = t result = t
while result.kind == tyGenericInst: result = lastSon(result) while result.kind == tyGenericInst: result = lastSon(result)
proc skipTypes(t: PType, kinds: TTypeKinds): PType =
result = t
while result.kind in kinds: result = lastSon(result)
proc isOrdinalType(t: PType): bool = proc isOrdinalType(t: PType): bool =
assert(t != nil) assert(t != nil)
# caution: uint, uint64 are no ordinal types! # caution: uint, uint64 are no ordinal types!
@ -410,18 +405,8 @@ const
"uint", "uint8", "uint16", "uint32", "uint64", "uint", "uint8", "uint16", "uint32", "uint64",
"bignum", "const ", "bignum", "const ",
"!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass", "!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass",
"ParametricTypeClass", "and", "or", "not", "any"] "ParametricTypeClass", "BuiltInTypeClass", "CompositeTypeClass",
"and", "or", "not", "any", "static", "TypeFromExpr"]
proc consToStr(t: PType): string =
if t.len > 0: result = t.typeToString
else: result = typeToStr[t.kind].strip
proc constraintsToStr(t: PType): string =
let sep = if tfAny in t.flags: " or " else: " and "
result = ""
for i in countup(0, t.len - 1):
if i > 0: result.add(sep)
result.add(t.sons[i].consToStr)
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
var t = typ var t = typ
@ -445,16 +430,26 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
add(result, ']') add(result, ']')
of tyTypeDesc: of tyTypeDesc:
if t.len == 0: result = "typedesc" if t.len == 0: result = "typedesc"
else: result = "typedesc[" & constraintsToStr(t) & "]" else: result = "typedesc[" & typeToString(t.sons[0]) & "]"
of tyStatic:
InternalAssert t.len > 0
result = "static[" & typeToString(t.sons[0]) & "]"
of tyTypeClass: of tyTypeClass:
if t.n != nil: return t.sym.owner.name.s InternalAssert t.sym != nil and t.sym.owner != nil
case t.len return t.sym.owner.name.s
of 0: result = "typeclass[]" of tyBuiltInTypeClass:
of 1: result = "typeclass[" & consToStr(t.sons[0]) & "]" return "TypeClass"
else: result = constraintsToStr(t) of tyAnd:
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
of tyOr:
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
of tyNot:
result = "not " & typeToString(t.sons[0])
of tyExpr: of tyExpr:
if t.len == 0: result = "expr" InternalAssert t.len == 0
else: result = "expr[" & constraintsToStr(t) & "]" result = "expr"
of tyFromExpr:
result = renderTree(t.n)
of tyArray: of tyArray:
if t.sons[0].kind == tyRange: if t.sons[0].kind == tyRange:
result = "array[" & rangeToStr(t.sons[0].n) & ", " & result = "array[" & rangeToStr(t.sons[0].n) & ", " &
@ -607,8 +602,11 @@ type
dcEqOrDistinctOf ## a equals b or a is distinct of b dcEqOrDistinctOf ## a equals b or a is distinct of b
TTypeCmpFlag* = enum TTypeCmpFlag* = enum
IgnoreTupleFields, IgnoreTupleFields
TypeDescExactMatch, IgnoreCC
ExactTypeDescValues
ExactGenericParams
ExactConstraints
AllowCommonBase AllowCommonBase
TTypeCmpFlags* = set[TTypeCmpFlag] TTypeCmpFlags* = set[TTypeCmpFlag]
@ -638,18 +636,20 @@ proc sameTypeOrNilAux(a, b: PType, c: var TSameTypeClosure): bool =
if a == nil or b == nil: result = false if a == nil or b == nil: result = false
else: result = sameTypeAux(a, b, c) else: result = sameTypeAux(a, b, c)
proc sameType*(a, b: PType, flags: TTypeCmpFlags = {}): bool =
var c = initSameTypeClosure()
c.flags = flags
result = sameTypeAux(a, b, c)
proc sameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool = proc sameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool =
if a == b: if a == b:
result = true result = true
else: else:
if a == nil or b == nil: result = false if a == nil or b == nil: result = false
else: else: result = sameType(a, b, flags)
var c = initSameTypeClosure()
c.flags = flags
result = sameTypeAux(a, b, c)
proc equalParam(a, b: PSym): TParamsEquality = proc equalParam(a, b: PSym): TParamsEquality =
if sameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}) and if sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}) and
exprStructuralEquivalent(a.constraint, b.constraint): exprStructuralEquivalent(a.constraint, b.constraint):
if a.ast == b.ast: if a.ast == b.ast:
result = paramsEqual result = paramsEqual
@ -663,6 +663,14 @@ proc equalParam(a, b: PSym): TParamsEquality =
else: else:
result = paramsNotEqual result = paramsNotEqual
proc sameConstraints(a, b: PNode): bool =
internalAssert a.len == b.len
for i in 1 .. <a.len:
if not exprStructuralEquivalent(a[i].sym.constraint,
b[i].sym.constraint):
return false
return true
proc equalParams(a, b: PNode): TParamsEquality = proc equalParams(a, b: PNode): TParamsEquality =
result = paramsEqual result = paramsEqual
var length = sonsLen(a) var length = sonsLen(a)
@ -685,7 +693,7 @@ proc equalParams(a, b: PNode): TParamsEquality =
return paramsNotEqual # paramsIncompatible; return paramsNotEqual # paramsIncompatible;
# continue traversal! If not equal, we can return immediately; else # continue traversal! If not equal, we can return immediately; else
# it stays incompatible # it stays incompatible
if not sameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {TypeDescExactMatch}): if not sameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {ExactTypeDescValues}):
if (a.sons[0].typ == nil) or (b.sons[0].typ == nil): if (a.sons[0].typ == nil) or (b.sons[0].typ == nil):
result = paramsNotEqual # one proc has a result, the other not is OK result = paramsNotEqual # one proc has a result, the other not is OK
else: else:
@ -829,9 +837,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
if a.kind != b.kind: return false if a.kind != b.kind: return false
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: tyInt..tyBigNum, tyStmt, tyExpr:
result = sameFlags(a, b) result = sameFlags(a, b)
of tyExpr: of tyStatic, tyFromExpr:
result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b) result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
of tyObject: of tyObject:
ifFastObjectTypeCheckFailed(a, b): ifFastObjectTypeCheckFailed(a, b):
@ -855,19 +863,25 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
result = sameTypeAux(lastSon(a), lastSon(b), c) result = sameTypeAux(lastSon(a), lastSon(b), c)
of tyTypeDesc: of tyTypeDesc:
if c.cmp == dcEqIgnoreDistinct: result = false if c.cmp == dcEqIgnoreDistinct: result = false
elif TypeDescExactMatch in c.flags: elif ExactTypeDescValues in c.flags:
cycleCheck() cycleCheck()
result = sameChildrenAux(x, y, c) and sameFlags(a, b) result = sameChildrenAux(x, y, c) and sameFlags(a, b)
else: else:
result = sameFlags(a, b) result = sameFlags(a, b)
of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, of tyGenericParam:
result = sameChildrenAux(a, b, c) and sameFlags(a, b)
if result and ExactGenericParams in c.flags:
result = a.sym.position == b.sym.position
of tyGenericInvokation, tyGenericBody, tySequence,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr,
tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter, tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter,
tyOrdinal, tyTypeClasses: tyOrdinal, tyTypeClasses:
cycleCheck() cycleCheck()
if a.kind == tyTypeClass and a.n != nil: return a.n == b.n
result = sameChildrenAux(a, b, c) and sameFlags(a, b) result = sameChildrenAux(a, b, c) and sameFlags(a, b)
if result and a.kind == tyProc: if result and a.kind == tyProc:
result = a.callConv == b.callConv result = ((IgnoreCC in c.flags) or a.callConv == b.callConv) and
((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n))
of tyRange: of tyRange:
cycleCheck() cycleCheck()
result = sameTypeOrNilAux(a.sons[0], b.sons[0], c) and result = sameTypeOrNilAux(a.sons[0], b.sons[0], c) and
@ -875,10 +889,6 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
sameValue(a.n.sons[1], b.n.sons[1]) sameValue(a.n.sons[1], b.n.sons[1])
of tyNone: result = false of tyNone: result = false
proc sameType*(x, y: PType): bool =
var c = initSameTypeClosure()
result = sameTypeAux(x, y, c)
proc sameBackendType*(x, y: PType): bool = proc sameBackendType*(x, y: PType): bool =
var c = initSameTypeClosure() var c = initSameTypeClosure()
c.flags.incl IgnoreTupleFields c.flags.incl IgnoreTupleFields
@ -976,42 +986,6 @@ proc isGenericAlias*(t: PType): bool =
proc skipGenericAlias*(t: PType): PType = proc skipGenericAlias*(t: PType): PType =
return if t.isGenericAlias: t.lastSon else: t return if t.isGenericAlias: t.lastSon else: t
proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool =
for i in countup(0, typeClass.sonsLen - 1):
let req = typeClass.sons[i]
var match = req.kind == skipTypes(t, {tyRange, tyGenericInst}).kind
if not match:
case req.kind
of tyGenericBody:
if t.kind == tyGenericInst and t.sons[0] == req:
match = true
idTablePut(bindings, typeClass, t)
of tyTypeClass:
match = matchTypeClass(bindings, req, t)
elif t.kind == tyTypeClass:
match = matchTypeClass(bindings, t, req)
elif t.kind in {tyObject} and req.len != 0:
# empty 'object' is fine as constraint in a type class
match = sameType(t, req)
if tfAny in typeClass.flags:
if match: return true
else:
if not match: return false
# if the loop finished without returning, either all constraints matched
# or none of them matched.
result = if tfAny in typeClass.flags: false else: true
if result == true:
idTablePut(bindings, typeClass, t)
proc matchTypeClass*(typeClass, typ: PType): bool =
var bindings: TIdTable
initIdTable(bindings)
result = matchTypeClass(bindings, typeClass, typ)
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})
@ -1039,14 +1013,15 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
if not result: break if not result: break
if result and t.sons[0] != nil: if result and t.sons[0] != nil:
result = typeAllowedAux(marker, t.sons[0], skResult, flags) result = typeAllowedAux(marker, t.sons[0], skResult, flags)
of tyExpr, tyStmt, tyTypeDesc: of tyExpr, tyStmt, tyTypeDesc, tyStatic:
result = true result = true
# XXX er ... no? these should not be allowed! # XXX er ... no? these should not be allowed!
of tyEmpty: of tyEmpty:
result = taField in flags result = taField in flags
of tyTypeClasses: of tyTypeClasses:
result = true result = true
of tyGenericBody, tyGenericParam, tyForward, tyNone, tyGenericInvokation: of tyGenericBody, tyGenericParam, tyGenericInvokation,
tyNone, tyForward, tyFromExpr:
result = false result = false
of tyNil: of tyNil:
result = kind == skConst result = kind == skConst
@ -1133,18 +1108,22 @@ proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt =
a = 1 a = 1
result = - 1 result = - 1
const
szIllegalRecursion* = -2
szUnknownSize* = -1
proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
var res, maxAlign, length, currOffset: BiggestInt var res, maxAlign, length, currOffset: BiggestInt
if typ.size == - 2: if typ.size == szIllegalRecursion:
# we are already computing the size of the type # we are already computing the size of the type
# --> illegal recursion in type # --> illegal recursion in type
return - 2 return szIllegalRecursion
if typ.size >= 0: if typ.size >= 0:
# size already computed # size already computed
result = typ.size result = typ.size
a = typ.align a = typ.align
return return
typ.size = - 2 # mark as being computed typ.size = szIllegalRecursion # mark as being computed
case typ.kind case typ.kind
of tyInt, tyUInt: of tyInt, tyUInt:
result = intSize result = intSize
@ -1176,7 +1155,9 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
result = ptrSize result = ptrSize
a = result a = result
of tyArray, tyArrayConstr: of tyArray, tyArrayConstr:
result = lengthOrd(typ.sons[0]) * computeSizeAux(typ.sons[1], a) let elemSize = computeSizeAux(typ.sons[1], a)
if elemSize < 0: return elemSize
result = lengthOrd(typ.sons[0]) * elemSize
of tyEnum: of tyEnum:
if firstOrd(typ) < 0: if firstOrd(typ) < 0:
result = 4 # use signed int32 result = 4 # use signed int32
@ -1227,11 +1208,12 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter: of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter:
result = computeSizeAux(lastSon(typ), a) result = computeSizeAux(lastSon(typ), a)
of tyTypeDesc: of tyTypeDesc:
result = (if typ.len == 1: computeSizeAux(typ.sons[0], a) else: -1) result = if typ.len == 1: computeSizeAux(typ.sons[0], a)
of tyProxy: result = 1 else: szUnknownSize
of tyForward: return szIllegalRecursion
else: else:
#internalError("computeSizeAux()") #internalError("computeSizeAux()")
result = - 1 result = szUnknownSize
typ.size = result typ.size = result
typ.align = int(a) typ.align = int(a)
@ -1248,9 +1230,9 @@ proc getSize(typ: PType): BiggestInt =
result = computeSize(typ) result = computeSize(typ)
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 =
result = t.kind in GenericTypes result = t.kind in GenericTypes + tyTypeClasses + {tyTypeDesc,tyFromExpr} or
t.kind == tyStatic and t.n == nil
proc containsGenericType*(t: PType): bool = proc containsGenericType*(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil) result = iterOverType(t, containsGenericTypeIter, nil)
@ -1315,7 +1297,7 @@ proc compatibleEffects*(formal, actual: PType): bool =
result = true result = true
proc isCompileTimeOnly*(t: PType): bool {.inline.} = proc isCompileTimeOnly*(t: PType): bool {.inline.} =
result = t.kind in {tyTypeDesc, tyExpr} result = t.kind in {tyTypeDesc, tyStatic}
proc containsCompileTimeOnly*(t: PType): bool = proc containsCompileTimeOnly*(t: PType): bool =
if isCompileTimeOnly(t): return true if isCompileTimeOnly(t): return true

View file

@ -791,7 +791,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
decodeBC(nkIntLit) decodeBC(nkIntLit)
let t1 = regs[rb].typ.skipTypes({tyTypeDesc}) let t1 = regs[rb].typ.skipTypes({tyTypeDesc})
let t2 = c.types[regs[rc].intVal.int] let t2 = c.types[regs[rc].intVal.int]
let match = if t2.kind == tyTypeClass: matchTypeClass(t2, t1) # XXX: This should use the standard isOpImpl
let match = if t2.kind == tyTypeClass: true
else: sameType(t1, t2) else: sameType(t1, t2)
regs[ra].intVal = ord(match) regs[ra].intVal = ord(match)
of opcSetLenSeq: of opcSetLenSeq:

View file

@ -832,7 +832,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
const const
atomicTypes = {tyBool, tyChar, atomicTypes = {tyBool, tyChar,
tyExpr, tyStmt, tyTypeDesc, tyExpr, tyStmt, tyTypeDesc, tyStatic,
tyEnum, tyEnum,
tyOrdinal, tyOrdinal,
tyRange, tyRange,
@ -1040,7 +1040,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
of tyFloat..tyFloat128: of tyFloat..tyFloat128:
result = newNodeIT(nkFloatLit, info, t) result = newNodeIT(nkFloatLit, info, t)
of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr,
tyStmt, tyTypeDesc, tyRef: tyStmt, tyTypeDesc, tyStatic, tyRef:
result = newNodeIT(nkNilLit, info, t) result = newNodeIT(nkNilLit, info, t)
of tyProc: of tyProc:
if t.callConv != ccClosure: if t.callConv != ccClosure:

View file

@ -3837,6 +3837,60 @@ This is a simple syntactic transformation into:
Special Types Special Types
============= =============
static[T]
---------
As their name suggests, static params must be known at compile-time:
.. code-block:: nimrod
proc precompiledRegex(pattern: static[string]): TRegEx =
var res {.global.} = re(pattern)
return res
precompiledRegex("/d+") # Replaces the call with a precompiled
# regex, stored in a global variable
precompiledRegex(paramStr(1)) # Error, command-line options
# are not known at compile-time
For the purposes of code generation, all static params are treated as
generic params - the proc will be compiled separately for each unique
supplied value (or combination of values).
Furthermore, the system module defines a `semistatic[T]` type than can be
used to declare procs accepting both static and run-time values, which can
optimize their body according to the supplied param using the `isStatic(p)`
predicate:
.. code-block:: nimrod
# The following proc will be compiled once for each unique static
# value and also once for the case handling all run-time values:
proc re(pattern: semistatic[string]): TRegEx =
when isStatic(pattern):
return precompiledRegex(pattern)
else:
return compile(pattern)
Static params can also appear in the signatures of generic types:
.. code-block:: nimrod
type
Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T]
# Please, note how `Number` is just a type constraint here, while
# `static[int]` requires us to supply a compile-time int value
AffineTransform2D[T] = Matrix[3, 3, T]
AffineTransform3D[T] = Matrix[4, 4, T]
AffineTransform3D[float] # OK
AffineTransform2D[string] # Error, `string` is not a `Number`
typedesc typedesc
-------- --------

View file

@ -59,7 +59,8 @@ type
nnkBindStmt, nnkMixinStmt, nnkUsingStmt, nnkBindStmt, nnkMixinStmt, nnkUsingStmt,
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkTypeClassTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkTupleTy, nnkTypeClassTy, nnkStaticTy,
nnkRecList, nnkRecCase, nnkRecWhen,
nnkRefTy, nnkPtrTy, nnkVarTy, nnkRefTy, nnkPtrTy, nnkVarTy,
nnkConstTy, nnkMutableTy, nnkConstTy, nnkMutableTy,
nnkDistinctTy, nnkDistinctTy,
@ -293,13 +294,12 @@ proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst".}
## if not `ex`: ## if not `ex`:
## echo `info` & ": Check failed: " & `expString` ## echo `info` & ": Check failed: " & `expString`
template emit*(e: expr[string]): stmt = when not defined(booting):
template emit*(e: static[string]): stmt =
## accepts a single string argument and treats it as nimrod code ## accepts a single string argument and treats it as nimrod code
## that should be inserted verbatim in the program ## that should be inserted verbatim in the program
## Example: ## Example:
## ##
## .. code-block:: nimrod
##
## emit("echo " & '"' & "hello world".toUpper & '"') ## emit("echo " & '"' & "hello world".toUpper & '"')
## ##
eval: result = e.parseStmt eval: result = e.parseStmt

View file

@ -144,7 +144,7 @@ proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.}
## be called before any possible `object branch transition`:idx:. ## be called before any possible `object branch transition`:idx:.
# for low and high the return type T may not be correct, but # for low and high the return type T may not be correct, but
# we handle that with compiler magic in SemLowHigh() # we handle that with compiler magic in semLowHigh()
proc high*[T](x: T): T {.magic: "High", noSideEffect.} proc high*[T](x: T): T {.magic: "High", noSideEffect.}
## returns the highest possible index of an array, a sequence, a string or ## returns the highest possible index of an array, a sequence, a string or
## the highest possible value of an ordinal value `x`. As a special ## the highest possible value of an ordinal value `x`. As a special
@ -422,10 +422,18 @@ proc incl*[T](x: var set[T], y: T) {.magic: "Incl", noSideEffect.}
## includes element ``y`` to the set ``x``. This is the same as ## includes element ``y`` to the set ``x``. This is the same as
## ``x = x + {y}``, but it might be more efficient. ## ``x = x + {y}``, but it might be more efficient.
template incl*[T](s: var set[T], flags: set[T]) =
## includes the set of flags to the set ``x``.
s = s + flags
proc excl*[T](x: var set[T], y: T) {.magic: "Excl", noSideEffect.} proc excl*[T](x: var set[T], y: T) {.magic: "Excl", noSideEffect.}
## excludes element ``y`` to the set ``x``. This is the same as ## excludes element ``y`` to the set ``x``. This is the same as
## ``x = x - {y}``, but it might be more efficient. ## ``x = x - {y}``, but it might be more efficient.
template excl*[T](s: var set[T], flags: set[T]) =
## excludes the set of flags to ``x``.
s = s - flags
proc card*[T](x: set[T]): int {.magic: "Card", noSideEffect.} proc card*[T](x: set[T]): int {.magic: "Card", noSideEffect.}
## returns the cardinality of the set ``x``, i.e. the number of elements ## returns the cardinality of the set ``x``, i.e. the number of elements
## in the set. ## in the set.
@ -2550,7 +2558,7 @@ proc raiseAssert*(msg: string) {.noinline.} =
sysFatal(EAssertionFailed, msg) sysFatal(EAssertionFailed, msg)
when true: when true:
proc hiddenRaiseAssert(msg: string) {.raises: [], tags: [].} = proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} =
# trick the compiler to not list ``EAssertionFailed`` when called # trick the compiler to not list ``EAssertionFailed`` when called
# by ``assert``. # by ``assert``.
type THide = proc (msg: string) {.noinline, raises: [], noSideEffect, type THide = proc (msg: string) {.noinline, raises: [], noSideEffect,
@ -2563,11 +2571,11 @@ template assert*(cond: bool, msg = "") =
## raises an ``EAssertionFailure`` exception. However, the compiler may ## raises an ``EAssertionFailure`` exception. However, the compiler may
## not generate any code at all for ``assert`` if it is advised to do so. ## not generate any code at all for ``assert`` if it is advised to do so.
## Use ``assert`` for debugging purposes only. ## Use ``assert`` for debugging purposes only.
bind instantiationInfo, hiddenRaiseAssert bind instantiationInfo
mixin failedAssertImpl
when compileOption("assertions"): when compileOption("assertions"):
{.line.}: {.line.}:
if not cond: if not cond: failedAssertImpl(astToStr(cond) & ' ' & msg)
hiddenRaiseAssert(astToStr(cond) & ' ' & msg)
template doAssert*(cond: bool, msg = "") = template doAssert*(cond: bool, msg = "") =
## same as `assert` but is always turned on and not affected by the ## same as `assert` but is always turned on and not affected by the
@ -2580,9 +2588,9 @@ template doAssert*(cond: bool, msg = "") =
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: inject.} {.pragma: inject.}
template onFailedAssert*(msg: expr, code: stmt): stmt = template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} =
## Sets an assertion failure handler that will intercept any assert statements ## Sets an assertion failure handler that will intercept any assert
## following `onFailedAssert` in the current lexical scope. ## statements following `onFailedAssert` in the current lexical scope.
## Can be defined multiple times in a single function. ## Can be defined multiple times in a single function.
## ##
## .. code-block:: nimrod ## .. code-block:: nimrod
@ -2599,8 +2607,8 @@ template onFailedAssert*(msg: expr, code: stmt): stmt =
## ##
## assert(...) ## assert(...)
## ##
template raiseAssert(msgIMPL: string): stmt = template failedAssertImpl(msgIMPL: string): stmt {.dirty, immediate.} =
let msg {.inject.} = msgIMPL let msg = msgIMPL
code code
proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
@ -2646,7 +2654,7 @@ when hostOS != "standalone":
x[j+i] = item[j] x[j+i] = item[j]
inc(j) inc(j)
proc compiles*(x: expr): bool {.magic: "Compiles", noSideEffect.} = proc compiles*(x): bool {.magic: "Compiles", noSideEffect.} =
## Special compile-time procedure that checks whether `x` can be compiled ## Special compile-time procedure that checks whether `x` can be compiled
## without any semantic error. ## without any semantic error.
## This can be used to check whether a type supports some operation: ## This can be used to check whether a type supports some operation:
@ -2680,3 +2688,13 @@ proc locals*(): TObject {.magic: "Locals", noSideEffect.} =
## the official signature says, the return type is not ``TObject`` but a ## the official signature says, the return type is not ``TObject`` but a
## tuple of a structure that depends on the current scope. ## tuple of a structure that depends on the current scope.
discard discard
when not defined(booting):
type
semistatic*[T] = static[T] | T
# indicates a param of proc specialized for each static value,
# but also accepting run-time values
template isStatic*(x): expr = compiles(static(x))
# checks whether `x` is a value known at compile-time

View file

@ -0,0 +1,6 @@
template accept(e: expr) =
static: assert(compiles(e))
template reject(e: expr) =
static: assert(not compiles(e))

View file

@ -16,10 +16,10 @@ type
TBar = tuple TBar = tuple
x, y: int x, y: int
template good(e: expr) = template accept(e: expr) =
static: assert(compiles(e)) static: assert(compiles(e))
template bad(e: expr) = template reject(e: expr) =
static: assert(not compiles(e)) static: assert(not compiles(e))
proc genericParamRepeated[T: typedesc](a: T, b: T) = proc genericParamRepeated[T: typedesc](a: T, b: T) =
@ -27,22 +27,22 @@ proc genericParamRepeated[T: typedesc](a: T, b: T) =
echo a.name echo a.name
echo b.name echo b.name
good(genericParamRepeated(int, int)) accept genericParamRepeated(int, int)
good(genericParamRepeated(float, float)) accept genericParamRepeated(float, float)
bad(genericParamRepeated(string, int)) reject genericParamRepeated(string, int)
bad(genericParamRepeated(int, float)) reject genericParamRepeated(int, float)
proc genericParamOnce[T: typedesc](a, b: T) = proc genericParamOnce[T: typedesc](a, b: T) =
static: static:
echo a.name echo a.name
echo b.name echo b.name
good(genericParamOnce(int, int)) accept genericParamOnce(int, int)
good(genericParamOnce(TFoo, TFoo)) accept genericParamOnce(TFoo, TFoo)
bad(genericParamOnce(string, int)) reject genericParamOnce(string, int)
bad(genericParamOnce(TFoo, float)) reject genericParamOnce(TFoo, float)
type type
type1 = typedesc type1 = typedesc
@ -50,42 +50,42 @@ type
proc typePairs(A, B: type1; C, D: type2) = nil proc typePairs(A, B: type1; C, D: type2) = nil
good(typePairs(int, int, TFoo, TFOO)) accept typePairs(int, int, TFoo, TFOO)
good(typePairs(TBAR, TBar, TBAR, TBAR)) accept typePairs(TBAR, TBar, TBAR, TBAR)
good(typePairs(int, int, string, string)) accept typePairs(int, int, string, string)
bad(typePairs(TBAR, TBar, TBar, TFoo)) reject typePairs(TBAR, TBar, TBar, TFoo)
bad(typePairs(string, int, TBAR, TBAR)) reject typePairs(string, int, TBAR, TBAR)
proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = nil proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = nil
good(typePairs2(int, int, TFoo, TFOO)) accept typePairs2(int, int, TFoo, TFOO)
good(typePairs2(TBAR, TBar, TBAR, TBAR)) accept typePairs2(TBAR, TBar, TBAR, TBAR)
good(typePairs2(int, int, string, string)) accept typePairs2(int, int, string, string)
bad(typePairs2(TBAR, TBar, TBar, TFoo)) reject typePairs2(TBAR, TBar, TBar, TFoo)
bad(typePairs2(string, int, TBAR, TBAR)) reject typePairs2(string, int, TBAR, TBAR)
proc dontBind(a: typedesc, b: typedesc) = proc dontBind(a: typedesc, b: typedesc) =
static: static:
echo a.name echo a.name
echo b.name echo b.name
good(dontBind(int, float)) accept dontBind(int, float)
good(dontBind(TFoo, TFoo)) accept dontBind(TFoo, TFoo)
proc dontBind2(a, b: typedesc) = nil proc dontBind2(a, b: typedesc) = nil
good(dontBind2(int, float)) accept dontBind2(int, float)
good(dontBind2(TBar, int)) accept dontBind2(TBar, int)
proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = nil proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = nil
good(bindArg(int, string, 10, 20, "test", "nest")) accept bindArg(int, string, 10, 20, "test", "nest")
good(bindArg(int, int, 10, 20, 30, 40)) accept bindArg(int, int, 10, 20, 30, 40)
bad(bindArg(int, string, 10, "test", "test", "nest")) reject bindArg(int, string, 10, "test", "test", "nest")
bad(bindArg(int, int, 10, 20, 30, "test")) reject bindArg(int, int, 10, 20, 30, "test")
bad(bindArg(int, string, 10.0, 20, "test", "nest")) reject bindArg(int, string, 10.0, 20, "test", "nest")
bad(bindArg(int, string, "test", "nest", 10, 20)) reject bindArg(int, string, "test", "nest", 10, 20)

View file

@ -0,0 +1,59 @@
template accept(e) =
static: assert(compiles(e))
template reject(e) =
static: assert(not compiles(e))
type
TFoo[T, U] = tuple
x: T
y: U
TBar[K] = TFoo[K, K]
TUserClass = int|string
TBaz = TBar[TUserClass]
var
vfoo: TFoo[int, string]
vbar: TFoo[string, string]
vbaz: TFoo[int, int]
vnotbaz: TFoo[TObject, TObject]
proc foo(x: TFoo) = echo "foo"
proc bar(x: TBar) = echo "bar"
proc baz(x: TBaz) = echo "baz"
accept foo(vfoo)
accept bar(vbar)
accept baz(vbar)
accept baz(vbaz)
reject baz(vnotbaz)
reject bar(vfoo)
# https://github.com/Araq/Nimrod/issues/517
type
TVecT*[T] = array[0..1, T]|array[0..2, T]|array[0..3, T]
TVec2* = array[0..1, float32]
proc f[T](a: TVecT[T], b: TVecT[T]): T = discard
var x: float = f([0.0'f32, 0.0'f32], [0.0'f32, 0.0'f32])
var y = f(TVec2([0.0'f32, 0.0'f32]), TVec2([0.0'f32, 0.0'f32]))
# https://github.com/Araq/Nimrod/issues/602
type
TTest = object
TTest2* = object
TUnion = TTest | TTest2
proc f(src: ptr TUnion, dst: ptr TUnion) =
echo("asd")
var tx: TTest
var ty: TTest2
accept f(addr tx, addr tx)
reject f(addr tx, addr ty)

View file

@ -1,6 +1,6 @@
discard """ discard """
file: "tgenericshardcases.nim" file: "tgenericshardcases.nim"
output: "int\nfloat\nint\nstring" output: "2\n5\n126\n3"
""" """
import typetraits import typetraits
@ -13,18 +13,24 @@ macro selectType(a, b: typedesc): typedesc =
type type
Foo[T] = object Foo[T] = object
data1: array[high(T), int] data1: array[T.high, int]
data2: array[1..typeNameLen(T), selectType(float, string)] data2: array[typeNameLen(T), float] # data3: array[0..T.typeNameLen, selectType(float, int)]
MyEnum = enum A, B, C,D MyEnum = enum A, B, C, D
var f1: Foo[MyEnum] var f1: Foo[MyEnum]
var f2: Foo[int8] var f2: Foo[int8]
static: echo high(f1.data1) # (D = 3) - 1 == 2
assert high(f1.data1) == D echo high(f1.data2) # (MyEnum.len = 6) - 1 == 5
assert high(f1.data2) == 6 # length of MyEnum
assert high(f2.data1) == 127 echo high(f2.data1) # 127 - 1 == 126
assert high(f2.data2) == 4 # length of int8 echo high(f2.data2) # int8.len - 1 == 3
#static:
# assert high(f1.data1) == ord(D)
# assert high(f1.data2) == 6 # length of MyEnum
# assert high(f2.data1) == 127
# assert high(f2.data2) == 4 # length of int8

View file

@ -0,0 +1,41 @@
discard """
output: '''0.0000000000000000e+00
0.0000000000000000e+00
0
0
0
'''
"""
include compilehelpers
type
Matrix*[M, N, T] = object
aij*: array[M, array[N, T]]
Matrix2*[T] = Matrix[range[0..1], range[0..1], T]
Matrix3*[T] = Matrix[range[0..2], range[0..2], T]
proc mn(x: Matrix): Matrix.T = x.aij[0][0]
proc m2(x: Matrix2): Matrix2.T = x.aij[0][0]
proc m3(x: Matrix3): auto = x.aij[0][0]
var
matn: Matrix[range[0..3], range[0..2], int]
mat2: Matrix2[int]
mat3: Matrix3[float]
echo m3(mat3)
echo mn(mat3)
echo m2(mat2)
echo mn(mat2)
echo mn(matn)
reject m3(mat2)
reject m3(matn)
reject m2(mat3)
reject m2(matn)

View file

@ -1,9 +0,0 @@
# Module A
var
lastId = 0
template genId*: expr =
inc(lastId)
lastId

View file

@ -1,13 +0,0 @@
discard """
file: "mbind4.nim"
line: 6
errormsg: "undeclared identifier: \'lastId\'"
"""
# Module B
import mbind4
echo genId()

View file

@ -1,5 +1,5 @@
discard """ discard """
line: 1840 line: 1855
file: "system.nim" file: "system.nim"
errormsg: "can raise an unlisted exception: ref EIO" errormsg: "can raise an unlisted exception: ref EIO"
""" """

View file

@ -14,4 +14,3 @@ type
y: Int y: Int
x: array[0..3, TIllegal] x: array[0..3, TIllegal]

View file

@ -6,5 +6,3 @@ discard """
type type
Uint8 = Uint8 #ERROR_MSG illegal recursion in type 'Uint8' Uint8 = Uint8 #ERROR_MSG illegal recursion in type 'Uint8'

View file

@ -1,6 +1,18 @@
discard """ discard """
output: '''some text output: '''----
Destructor called!''' myobj constructed
myobj destroyed
----
mygeneric1 constructed
mygeneric1 destroyed
----
mygeneric2 constructed
mygeneric2 destroyed
myobj destroyed
----
mygeneric3 constructed
mygeneric1 destroyed
'''
""" """
type type
@ -8,19 +20,65 @@ type
x, y: int x, y: int
p: pointer p: pointer
TMyGeneric1[T] = object
x: T
TMyGeneric2[A, B] = object
x: A
y: B
TMyGeneric3[A, B, C] = object
x: A
y: B
z: C
proc destruct(o: var TMyObj) {.destructor.} = proc destruct(o: var TMyObj) {.destructor.} =
if o.p != nil: dealloc o.p if o.p != nil: dealloc o.p
echo "Destructor called!" echo "myobj destroyed"
proc destroy(o: var TMyGeneric1) {.destructor.} =
echo "mygeneric1 destroyed"
proc destroy[A, B](o: var TMyGeneric2[A, B]) {.destructor.} =
echo "mygeneric2 destroyed"
proc open: TMyObj = proc open: TMyObj =
# allow for superfluous () # allow for superfluous ()
result = (TMyObj(x: 1, y: 2, p: alloc(3))) result = (TMyObj(x: 1, y: 2, p: alloc(3)))
proc `$`(x: TMyObj): string = $x.y proc `$`(x: TMyObj): string = $x.y
proc main() = proc myobj() =
var x = open() var x = open()
echo "some text" echo "myobj constructed"
proc mygeneric1() =
var x = TMyGeneric1[int](x: 10)
echo "mygeneric1 constructed"
proc mygeneric2[T](val: T) =
var
a = open()
b = TMyGeneric2[int, T](x: 10, y: val)
c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
echo "mygeneric2 constructed"
proc mygeneric3 =
var x = TMyGeneric3[int, string, TMyGeneric1[int]](
x: 10, y: "test", z: TMyGeneric1[int](x: 10))
echo "mygeneric3 constructed"
echo "----"
myobj()
echo "----"
mygeneric1()
echo "----"
mygeneric2[int](10)
echo "----"
mygeneric3()
main()

View file

@ -0,0 +1,51 @@
discard """
output: '''
WARNING: false first asseertion from bar
ERROR: false second assertion from bar
-1
tests/run/tfailedassert.nim:27 false assertion from foo
'''
"""
type
TLineInfo = tuple[filename: string, line: int]
TMyError = object of E_Base
lineinfo: TLineInfo
EMyError = ref TMyError
# module-wide policy to change the failed assert
# exception type in order to include a lineinfo
onFailedAssert(msg):
var e = new(TMyError)
e.msg = msg
e.lineinfo = instantiationInfo(-2)
raise e
proc foo =
assert(false, "assertion from foo")
proc bar: int =
# local overrides that are active only
# in this proc
onFailedAssert(msg): echo "WARNING: " & msg
assert(false, "first asseertion from bar")
onFailedAssert(msg):
echo "ERROR: " & msg
return -1
assert(false, "second assertion from bar")
return 10
echo("")
echo(bar())
try:
foo()
except:
let e = EMyError(getCurrentException())
echo e.lineinfo.filename, ":", e.lineinfo.line, " ", e.msg

View file

@ -1,11 +1,11 @@
discard """ discard """
msg: "test 1\ntest 2" msg: "test 1\ntest 2\ntest 3"
output: "TEST 1\nTEST 2\nTEST 2" output: "TEST 1\nTEST 2\nTEST 3"
""" """
import strutils import strutils
proc foo(s: expr[string]): string = proc foo(s: static[string]): string =
static: echo s static: echo s
const R = s.toUpper const R = s.toUpper
@ -13,5 +13,5 @@ proc foo(s: expr[string]): string =
echo foo("test 1") echo foo("test 1")
echo foo("test 2") echo foo("test 2")
echo foo("test " & $2) echo foo("test " & $3)

24
tests/run/tsemistatic.nim Normal file
View file

@ -0,0 +1,24 @@
discard """
msg: "static 10\ndynamic\nstatic 20\n"
output: "s\nd\nd\ns"
"""
proc foo(x: semistatic[int]) =
when isStatic(x):
static: echo "static ", x
echo "s"
else:
static: echo "dynamic"
echo "d"
foo 10
var
x = 10
y: int
foo x
foo y
foo 20

View file

@ -4,15 +4,15 @@ discard """
""" """
type type
TFoo[T; Val: expr[string]] = object TFoo[T; Val: static[string]] = object
data: array[4, T] data: array[4, T]
TBar[T; I: expr[int]] = object TBar[T; I: static[int]] = object
data: array[I, T] data: array[I, T]
TA1[T; I: expr[int]] = array[I, T] TA1[T; I: static[int]] = array[I, T]
TA2[T; I: expr[int]] = array[0..I, T] # TA2[T; I: static[int]] = array[0..I, T]
TA3[T; I: expr[int]] = array[I-1, T] # TA3[T; I: static[int]] = array[I-1, T]
proc takeFoo(x: TFoo) = proc takeFoo(x: TFoo) =
echo "abracadabra" echo "abracadabra"
@ -25,7 +25,7 @@ var y: TBar[float, 4]
echo high(y.data) echo high(y.data)
var var
t1: TA1 t1: TA1[float, 1]
t2: TA2 # t2: TA2[string, 4]
t3: TA3 # t3: TA3[int, 10]

View file

@ -1,6 +1,6 @@
discard """ discard """
msg: "int\nstring\nTBar[int]" msg: "int\nstring\nTBar[int]"
output: "int\nstring\nTBar[int]\nint\nrange 0..2\nstring" output: "int\nstring\nTBar[int]\nint\nrange 0..2(int)\nstring"
""" """
import typetraits import typetraits

View file

@ -8,25 +8,11 @@ import
# This macro mimics the using statement from C# # This macro mimics the using statement from C#
# #
# XXX: # It's kept only as a test for the macro system
# It doen't match the C# version exactly yet. # Nimrod's destructors offer a mechanism for automatic
# In particular, it's not recursive, which prevents it from dealing # disposal of resources.
# with exceptions thrown from the variable initializers when multiple.
# variables are used.
# #
# Also, since nimrod relies less on exceptions in general, a more macro autoClose(e: expr): stmt {.immediate.} =
# idiomatic definition could be:
# var x = init()
# if opened(x):
# try:
# body
# finally:
# close(x)
#
# `opened` here could be an overloaded proc which any type can define.
# A common practice can be returing an Optional[Resource] obj for which
# `opened` is defined to `optional.hasValue`
macro using(e: expr): stmt {.immediate.} =
let e = callsite() let e = callsite()
if e.len != 3: if e.len != 3:
error "Using statement: unexpected number of arguments. Got " & error "Using statement: unexpected number of arguments. Got " &
@ -97,7 +83,7 @@ proc close(r: var TResource) =
proc use(r: var TResource) = proc use(r: var TResource) =
write(stdout, "Using " & r.field & ".") write(stdout, "Using " & r.field & ".")
using(r = openResource("test")): autoClose(r = openResource("test")):
use r use r

View file

@ -42,7 +42,6 @@ version 0.9.x
- macros as type pragmas - macros as type pragmas
- implicit deref for parameter matching - implicit deref for parameter matching
- lazy overloading resolution: - lazy overloading resolution:
* get rid of ``expr[typ]``, use perhaps ``static[typ]`` instead
* special case ``tyStmt`` * special case ``tyStmt``
- FFI: - FFI:
* test libffi on windows * test libffi on windows

View file

@ -33,6 +33,10 @@ Changes affecting backwards compatibility
- The symbol binding rules for clean templates changed: ``bind`` for any - The symbol binding rules for clean templates changed: ``bind`` for any
symbol that's not a parameter is now the default. ``mixin`` can be used symbol that's not a parameter is now the default. ``mixin`` can be used
to require instantiation scope for a symbol. to require instantiation scope for a symbol.
- ``quoteIfContainsWhite`` now escapes argument in such way that it can be safely
passed to shell, instead of just adding double quotes.
- ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``,
``dumpTreeImm`` and ``dumpLispImm`` are now deprecated.
Compiler Additions Compiler Additions
@ -56,15 +60,15 @@ Language Additions
- Arrays can now be declared with a single integer literal ``N`` instead of a - Arrays can now be declared with a single integer literal ``N`` instead of a
range; the range is then ``0..N-1``. range; the range is then ``0..N-1``.
- ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``,
``dumpTreeImm`` and ``dumpLispImm`` are now deprecated.
- Added ``requiresInit`` pragma to enforce explicit initialization. - Added ``requiresInit`` pragma to enforce explicit initialization.
- Added ``using statement`` for better authoring domain-specific languages and
OOP-like syntactic sugar.
- Added ``delegator pragma`` for handling calls to missing procs and fields at
compile-time.
- Support for user-defined type classes has been added.
- Exported templates are allowed to access hidden fields. - Exported templates are allowed to access hidden fields.
- The ``using statement`` enables you to more easily author domain-specific
languages and libraries providing OOP-like syntactic sugar.
- Added a new ``delegator pragma`` for handling calls to missing procs and
fields at compile-time.
- The overload resolution now supports ``static[T]`` params that must be
evaluatable at compile-time.
- Support for user-defined type classes have been added.
Tools improvements Tools improvements