Merge branch 'araq2' into devel

This commit is contained in:
Araq 2017-05-17 22:13:57 +02:00
commit 8f67b90997
54 changed files with 2479 additions and 340 deletions

View file

@ -224,7 +224,7 @@ type
TNodeKinds* = set[TNodeKind] TNodeKinds* = set[TNodeKind]
type type
TSymFlag* = enum # already 32 flags! TSymFlag* = enum # already 33 flags!
sfUsed, # read access of sym (for warnings) or simply used sfUsed, # read access of sym (for warnings) or simply used
sfExported, # symbol is exported from module sfExported, # symbol is exported from module
sfFromGeneric, # symbol is instantiation of a generic; this is needed sfFromGeneric, # symbol is instantiation of a generic; this is needed
@ -452,10 +452,12 @@ type
nfExprCall # this is an attempt to call a regular expression nfExprCall # this is an attempt to call a regular expression
nfIsRef # this node is a 'ref' node; used for the VM nfIsRef # this node is a 'ref' node; used for the VM
nfPreventCg # this node should be ignored by the codegen nfPreventCg # this node should be ignored by the codegen
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
TNodeFlags* = set[TNodeFlag] TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 30) TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that)
tfVarargs, # procedure has C styled varargs tfVarargs, # procedure has C styled varargs
# tyArray type represeting a varargs list
tfNoSideEffect, # procedure type does not allow side effects tfNoSideEffect, # procedure type does not allow side effects
tfFinal, # is the object final? tfFinal, # is the object final?
tfInheritable, # is the object inheritable? tfInheritable, # is the object inheritable?
@ -506,6 +508,9 @@ type
tfTriggersCompileTime # uses the NimNode type which make the proc tfTriggersCompileTime # uses the NimNode type which make the proc
# implicitly '.compiletime' # implicitly '.compiletime'
tfRefsAnonObj # used for 'ref object' and 'ptr object' tfRefsAnonObj # used for 'ref object' and 'ptr object'
tfCovariant # covariant generic param mimicing a ptr type
tfWeakCovariant # covariant generic param mimicing a seq/array type
tfContravariant # contravariant generic param
TTypeFlags* = set[TTypeFlag] TTypeFlags* = set[TTypeFlag]
@ -1002,15 +1007,17 @@ proc add*(father, son: PNode) =
if isNil(father.sons): father.sons = @[] if isNil(father.sons): father.sons = @[]
add(father.sons, son) add(father.sons, son)
proc `[]`*(n: PNode, i: int): PNode {.inline.} = type Indexable = PNode | PType
result = n.sons[i]
template `[]`*(n: Indexable, i: int): Indexable =
n.sons[i]
template `-|`*(b, s: untyped): untyped = template `-|`*(b, s: untyped): untyped =
(if b >= 0: b else: s.len + b) (if b >= 0: b else: s.len + b)
# son access operators with support for negative indices # son access operators with support for negative indices
template `{}`*(n: PNode, i: int): untyped = n[i -| n] template `{}`*(n: Indexable, i: int): untyped = n[i -| n]
template `{}=`*(n: PNode, i: int, s: PNode) = template `{}=`*(n: Indexable, i: int, s: Indexable) =
n.sons[i -| n] = s n.sons[i -| n] = s
when defined(useNodeIds): when defined(useNodeIds):
@ -1346,6 +1353,9 @@ proc initIdTable*(x: var TIdTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
proc newIdTable*: TIdTable =
initIdTable(result)
proc resetIdTable*(x: var TIdTable) = proc resetIdTable*(x: var TIdTable) =
x.counter = 0 x.counter = 0
# clear and set to old initial size: # clear and set to old initial size:

View file

@ -70,6 +70,8 @@ proc debug*(n: PNode) {.deprecated.}
template mdbg*: bool {.dirty.} = template mdbg*: bool {.dirty.} =
when compiles(c.module): when compiles(c.module):
c.module.fileIdx == gProjectMainIdx c.module.fileIdx == gProjectMainIdx
elif compiles(c.c.module):
c.c.module.fileIdx == gProjectMainIdx
elif compiles(m.c.module): elif compiles(m.c.module):
m.c.module.fileIdx == gProjectMainIdx m.c.module.fileIdx == gProjectMainIdx
elif compiles(cl.c.module): elif compiles(cl.c.module):
@ -79,6 +81,8 @@ template mdbg*: bool {.dirty.} =
p.lex.fileIdx == gProjectMainIdx p.lex.fileIdx == gProjectMainIdx
else: else:
p.module.module.fileIdx == gProjectMainIdx p.module.module.fileIdx == gProjectMainIdx
elif compiles(m.module.fileIdx):
m.module.fileIdx == gProjectMainIdx
elif compiles(L.fileIdx): elif compiles(L.fileIdx):
L.fileIdx == gProjectMainIdx L.fileIdx == gProjectMainIdx
else: else:

View file

@ -269,7 +269,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
# little HACK to support the new 'var T' as return type: # little HACK to support the new 'var T' as return type:
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
return return
let ty = skipTypes(dest.t, abstractRange) let ty = skipTypes(dest.t, abstractRange + tyUserTypeClasses)
case ty.kind case ty.kind
of tyRef: of tyRef:
genRefAssign(p, dest, src, flags) genRefAssign(p, dest, src, flags)
@ -758,7 +758,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
genRecordFieldAux(p, e, d, a) genRecordFieldAux(p, e, d, a)
var r = rdLoc(a) var r = rdLoc(a)
var f = e.sons[1].sym var f = e.sons[1].sym
let ty = skipTypes(a.t, abstractInst) let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses)
if ty.kind == tyTuple: if ty.kind == tyTuple:
# we found a unique tuple type which lacks field information # we found a unique tuple type which lacks field information
# so we use Field$i # so we use Field$i
@ -2205,7 +2205,7 @@ proc getNullValueAux(p: BProc; obj, cons: PNode, result: var Rope) =
let field = obj.sym let field = obj.sym
for i in 1..<cons.len: for i in 1..<cons.len:
if cons[i].kind == nkExprColonExpr: if cons[i].kind == nkExprColonExpr:
if cons[i][0].sym == field: if cons[i][0].sym.name == field.name:
result.add genConstExpr(p, cons[i][1]) result.add genConstExpr(p, cons[i][1])
return return
elif i == field.position: elif i == field.position:

View file

@ -164,7 +164,7 @@ proc mapType(typ: PType): TCTypeKind =
of tySet: result = mapSetType(typ) of tySet: result = mapSetType(typ)
of tyOpenArray, tyArray, tyVarargs: result = ctArray of tyOpenArray, tyArray, tyVarargs: result = ctArray
of tyObject, tyTuple: result = ctStruct of tyObject, tyTuple: result = ctStruct
of tyUserTypeClass, tyUserTypeClassInst: of tyUserTypeClasses:
internalAssert typ.isResolvedUserTypeClass internalAssert typ.isResolvedUserTypeClass
return mapType(typ.lastSon) return mapType(typ.lastSon)
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal, of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
@ -1094,7 +1094,7 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
proc genTypeInfo(m: BModule, t: PType): Rope = proc genTypeInfo(m: BModule, t: PType): Rope =
let origType = t let origType = t
var t = skipTypes(origType, irrelevantForBackend) var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
let sig = hashType(origType) let sig = hashType(origType)
result = m.typeInfoMarker.getOrDefault(sig) result = m.typeInfoMarker.getOrDefault(sig)
@ -1131,6 +1131,9 @@ proc genTypeInfo(m: BModule, t: PType): Rope =
of tyStatic: of tyStatic:
if t.n != nil: result = genTypeInfo(m, lastSon t) if t.n != nil: result = genTypeInfo(m, lastSon t)
else: internalError("genTypeInfo(" & $t.kind & ')') else: internalError("genTypeInfo(" & $t.kind & ')')
of tyUserTypeClasses:
internalAssert t.isResolvedUserTypeClass
return genTypeInfo(m, t.lastSon)
of tyProc: of tyProc:
if t.callConv != ccClosure: if t.callConv != ccClosure:
genTypeInfoAuxBase(m, t, t, result, rope"0") genTypeInfoAuxBase(m, t, t, result, rope"0")

View file

@ -103,3 +103,4 @@ proc initDefines*() =
defineSymbol("nimNewShiftOps") defineSymbol("nimNewShiftOps")
defineSymbol("nimDistros") defineSymbol("nimDistros")
defineSymbol("nimHasCppDefine") defineSymbol("nimHasCppDefine")
defineSymbol("nimGenericInOutFlags")

View file

@ -90,7 +90,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
result = newNodeI(nkArgList, n.info) result = newNodeI(nkArgList, n.info)
for i in 1 .. givenRegularParams: for i in 1 .. givenRegularParams:
result.addSon n.sons[i] result.addSon n[i]
# handle parameters with default values, which were # handle parameters with default values, which were
# not supplied by the user # not supplied by the user

View file

@ -113,6 +113,7 @@ type
errGenericLambdaNotAllowed, errGenericLambdaNotAllowed,
errProcHasNoConcreteType, errProcHasNoConcreteType,
errCompilerDoesntSupportTarget, errCompilerDoesntSupportTarget,
errInOutFlagNotExtern,
errUser, errUser,
warnCannotOpenFile, warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@ -380,6 +381,7 @@ const
"of the generic paramers can be inferred from the expected signature.", "of the generic paramers can be inferred from the expected signature.",
errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.", errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.",
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types",
errUser: "$1", errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\'", warnCannotOpenFile: "cannot open \'$1\'",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",

View file

@ -67,6 +67,8 @@ proc parseTry(p: var TParser; isExpr: bool): PNode
proc parseCase(p: var TParser): PNode proc parseCase(p: var TParser): PNode
proc parseStmtPragma(p: var TParser): PNode proc parseStmtPragma(p: var TParser): PNode
proc parsePragma(p: var TParser): PNode proc parsePragma(p: var TParser): PNode
proc postExprBlocks(p: var TParser, x: PNode): PNode
proc parseExprStmt(p: var TParser): PNode
# implementation # implementation
proc getTok(p: var TParser) = proc getTok(p: var TParser) =
@ -194,7 +196,6 @@ proc newIdentNodeP(ident: PIdent, p: TParser): PNode =
proc parseExpr(p: var TParser): PNode proc parseExpr(p: var TParser): PNode
proc parseStmt(p: var TParser): PNode proc parseStmt(p: var TParser): PNode
proc parseTypeDesc(p: var TParser): PNode proc parseTypeDesc(p: var TParser): PNode
proc parseDoBlocks(p: var TParser, call: PNode)
proc parseParamList(p: var TParser, retColon = true): PNode proc parseParamList(p: var TParser, retColon = true): PNode
proc isSigilLike(tok: TToken): bool {.inline.} = proc isSigilLike(tok: TToken): bool {.inline.} =
@ -365,6 +366,9 @@ proc colonOrEquals(p: var TParser, a: PNode): PNode =
proc exprColonEqExpr(p: var TParser): PNode = proc exprColonEqExpr(p: var TParser): PNode =
#| exprColonEqExpr = expr (':'|'=' expr)? #| exprColonEqExpr = expr (':'|'=' expr)?
var a = parseExpr(p) var a = parseExpr(p)
if p.tok.tokType == tkDo:
result = postExprBlocks(p, a)
else:
result = colonOrEquals(p, a) result = colonOrEquals(p, a)
proc exprList(p: var TParser, endTok: TTokType, result: PNode) = proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
@ -520,7 +524,9 @@ proc parsePar(p: var TParser): PNode =
result.add(parseStmtPragma(p)) result.add(parseStmtPragma(p))
elif p.tok.tokType != tkParRi: elif p.tok.tokType != tkParRi:
var a = simpleExpr(p) var a = simpleExpr(p)
if p.tok.tokType == tkEquals: if p.tok.tokType == tkDo:
result = postExprBlocks(p, a)
elif p.tok.tokType == tkEquals:
# special case: allow assignments # special case: allow assignments
let asgn = newNodeP(nkAsgn, p) let asgn = newNodeP(nkAsgn, p)
getTok(p) getTok(p)
@ -669,7 +675,6 @@ proc namedParams(p: var TParser, callee: PNode,
# progress guaranteed # progress guaranteed
exprColonEqExprListAux(p, endTok, result) exprColonEqExprListAux(p, endTok, result)
proc parseMacroColon(p: var TParser, x: PNode): PNode
proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
#| | doBlocks #| | doBlocks
@ -696,14 +701,6 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
result = namedParams(p, result, nkCall, tkParRi) result = namedParams(p, result, nkCall, tkParRi)
if result.len > 1 and result.sons[1].kind == nkExprColonExpr: if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
result.kind = nkObjConstr result.kind = nkObjConstr
elif p.tok.tokType == tkDo:
parseDoBlocks(p, result)
of tkDo:
# progress guaranteed
var a = result
result = newNodeP(nkCall, p)
addSon(result, a)
parseDoBlocks(p, result)
of tkDot: of tkDot:
# progress guaranteed # progress guaranteed
result = dotExpr(p, result) result = dotExpr(p, result)
@ -735,10 +732,7 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
if p.tok.tokType != tkComma: break if p.tok.tokType != tkComma: break
getTok(p) getTok(p)
optInd(p, x) optInd(p, x)
if p.tok.tokType == tkDo: result = postExprBlocks(p, result)
parseDoBlocks(p, result)
else:
result = parseMacroColon(p, result)
break break
else: else:
break break
@ -977,16 +971,9 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
let params = parseParamList(p, retColon=false) let params = parseParamList(p, retColon=false)
let pragmas = optPragmas(p) let pragmas = optPragmas(p)
colcom(p, result) colcom(p, result)
result = newProcNode(nkDo, info, parseStmt(p), result = parseStmt(p)
params = params, if params.kind != nkEmpty:
pragmas = pragmas) result = newProcNode(nkDo, info, result, params = params, pragmas = pragmas)
proc parseDoBlocks(p: var TParser, call: PNode) =
#| doBlocks = doBlock ^* IND{=}
while sameOrNoInd(p) and p.tok.tokType == tkDo:
let info = parLineInfo(p)
getTok(p)
addSon(call, parseDoBlock(p, info))
proc parseProcExpr(p: var TParser, isExpr: bool): PNode = proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
#| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
@ -1162,49 +1149,75 @@ proc makeCall(n: PNode): PNode =
result = newNodeI(nkCall, n.info) result = newNodeI(nkCall, n.info)
result.add n result.add n
proc parseMacroColon(p: var TParser, x: PNode): PNode = proc postExprBlocks(p: var TParser, x: PNode): PNode =
#| macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt #| postExprBlocks = ':' stmt? ( IND{=} doBlock
#| | IND{=} 'of' exprList ':' stmt
#| | IND{=} 'elif' expr ':' stmt #| | IND{=} 'elif' expr ':' stmt
#| | IND{=} 'except' exprList ':' stmt #| | IND{=} 'except' exprList ':' stmt
#| | IND{=} 'else' ':' stmt )* #| | IND{=} 'else' ':' stmt )*
result = x result = x
if p.tok.tokType == tkColon and p.tok.indent < 0: if p.tok.indent >= 0: return
var
openingParams = emptyNode
openingPragmas = emptyNode
if p.tok.tokType == tkDo:
getTok(p)
openingParams = parseParamList(p, retColon=false)
openingPragmas = optPragmas(p)
if p.tok.tokType == tkColon:
result = makeCall(result) result = makeCall(result)
getTok(p) getTok(p)
skipComment(p, result) skipComment(p, result)
let stmtList = newNodeP(nkStmtList, p)
if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
let body = parseStmt(p) var stmtList = newNodeP(nkStmtList, p)
stmtList.add body stmtList.add parseStmt(p)
#addSon(result, makeStmtList(body)) # to keep backwards compatibility (see tests/vm/tstringnil)
# progress guaranteed if stmtList[0].kind == nkStmtList: stmtList = stmtList[0]
stmtList.flags.incl nfBlockArg
if openingParams.kind != nkEmpty:
result.add newProcNode(nkDo, stmtList.info, stmtList,
params = openingParams, pragmas = openingPragmas)
else:
result.add stmtList
while sameInd(p): while sameInd(p):
var b: PNode var nextBlock: PNode
case p.tok.tokType let nextToken = p.tok.tokType
of tkOf: if nextToken == tkDo:
b = newNodeP(nkOfBranch, p) let info = parLineInfo(p)
exprList(p, tkColon, b)
of tkElif:
b = newNodeP(nkElifBranch, p)
getTok(p) getTok(p)
optInd(p, b) nextBlock = parseDoBlock(p, info)
addSon(b, parseExpr(p)) else:
case nextToken:
of tkOf:
nextBlock = newNodeP(nkOfBranch, p)
exprList(p, tkColon, nextBlock)
of tkElif:
nextBlock = newNodeP(nkElifBranch, p)
getTok(p)
optInd(p, nextBlock)
nextBlock.addSon parseExpr(p)
of tkExcept: of tkExcept:
b = newNodeP(nkExceptBranch, p) nextBlock = newNodeP(nkExceptBranch, p)
exprList(p, tkColon, b) exprList(p, tkColon, nextBlock)
of tkElse: of tkElse:
b = newNodeP(nkElse, p) nextBlock = newNodeP(nkElse, p)
getTok(p) getTok(p)
else: break else: break
eat(p, tkColon) eat(p, tkColon)
addSon(b, parseStmt(p)) nextBlock.addSon parseStmt(p)
addSon(stmtList, b)
if b.kind == nkElse: break nextBlock.flags.incl nfBlockArg
if stmtList.len == 1 and stmtList[0].kind == nkStmtList: result.add nextBlock
# to keep backwards compatibility (see tests/vm/tstringnil)
result.add stmtList[0] if nextBlock.kind == nkElse: break
else: else:
result.add stmtList if openingParams.kind != nkEmpty:
parMessage(p, errTokenExpected, ":")
proc parseExprStmt(p: var TParser): PNode = proc parseExprStmt(p: var TParser): PNode =
#| exprStmt = simpleExpr #| exprStmt = simpleExpr
@ -1219,12 +1232,7 @@ proc parseExprStmt(p: var TParser): PNode =
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)
var b = parseExpr(p) var b = parseExpr(p)
if p.tok.tokType == tkColon and p.tok.indent < 0: b = postExprBlocks(p, b)
if b.kind != nkEmpty:
let call = makeCall(b)
call.add parseDoBlock(p, parLineInfo(p))
parseDoBlocks(p, call)
b = call
addSon(result, a) addSon(result, a)
addSon(result, b) addSon(result, b)
else: else:
@ -1247,11 +1255,7 @@ proc parseExprStmt(p: var TParser): PNode =
optInd(p, result) optInd(p, result)
else: else:
result = a result = a
if p.tok.tokType == tkDo and p.tok.indent < 0: result = postExprBlocks(p, result)
result = makeCall(result)
parseDoBlocks(p, result)
return result
result = parseMacroColon(p, result)
proc parseModuleName(p: var TParser, kind: TNodeKind): PNode = proc parseModuleName(p: var TParser, kind: TNodeKind): PNode =
result = parseExpr(p) result = parseExpr(p)
@ -1341,7 +1345,9 @@ proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode =
# NL terminates: # NL terminates:
addSon(result, ast.emptyNode) addSon(result, ast.emptyNode)
else: else:
addSon(result, parseExpr(p)) var e = parseExpr(p)
e = postExprBlocks(p, e)
addSon(result, e)
proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
#| condStmt = expr colcom stmt COMMENT? #| condStmt = expr colcom stmt COMMENT?
@ -1514,6 +1520,13 @@ proc parseGenericParam(p: var TParser): PNode =
# progress guaranteed # progress guaranteed
while true: while true:
case p.tok.tokType case p.tok.tokType
of tkIn, tkOut:
let x = p.lex.cache.getIdent(if p.tok.tokType == tkIn: "in" else: "out")
a = newNodeP(nkPrefix, p)
a.addSon newIdentNodeP(x, p)
getTok(p)
expectIdent(p)
a.addSon(parseSymbol(p))
of tkSymbol, tkAccent: of tkSymbol, tkAccent:
a = parseSymbol(p) a = parseSymbol(p)
if a.kind == nkEmpty: return if a.kind == nkEmpty: return
@ -1542,7 +1555,7 @@ proc parseGenericParamList(p: var TParser): PNode =
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)
# progress guaranteed # progress guaranteed
while p.tok.tokType in {tkSymbol, tkAccent}: while p.tok.tokType in {tkSymbol, tkAccent, tkIn, tkOut}:
var a = parseGenericParam(p) var a = parseGenericParam(p)
addSon(result, a) addSon(result, a)
if p.tok.tokType notin {tkComma, tkSemiColon}: break if p.tok.tokType notin {tkComma, tkSemiColon}: break
@ -1893,14 +1906,7 @@ proc parseVariable(p: var TParser): PNode =
#| variable = (varTuple / identColonEquals) colonBody? indAndComment #| variable = (varTuple / identColonEquals) colonBody? indAndComment
if p.tok.tokType == tkParLe: result = parseVarTuple(p) if p.tok.tokType == tkParLe: result = parseVarTuple(p)
else: result = parseIdentColonEquals(p, {withPragma}) else: result = parseIdentColonEquals(p, {withPragma})
if p.tok.tokType == tkColon and p.tok.indent < 0: result{-1} = postExprBlocks(p, result{-1})
let last = result.len-1
let ex = result.sons[last]
if ex.kind != nkEmpty:
let call = makeCall(ex)
call.add parseDoBlock(p, parLineInfo(p))
parseDoBlocks(p, call)
result.sons[last] = call
indAndComment(p, result) indAndComment(p, result)
proc parseBind(p: var TParser, k: TNodeKind): PNode = proc parseBind(p: var TParser, k: TNodeKind): PNode =

View file

@ -1336,6 +1336,11 @@ proc parseGenericParam(p: var TParser): PNode =
result = newNodeP(nkIdentDefs, p) result = newNodeP(nkIdentDefs, p)
while true: while true:
case p.tok.tokType case p.tok.tokType
of tkIn, tkOut:
let t = p.tok.tokType
getTok(p)
expectIdent(p)
a = parseSymbol(p)
of tkSymbol, tkAccent: of tkSymbol, tkAccent:
a = parseSymbol(p) a = parseSymbol(p)
if a.kind == nkEmpty: return if a.kind == nkEmpty: return

View file

@ -351,8 +351,8 @@ when false:
for i in 0 ..< n.safeLen: for i in 0 ..< n.safeLen:
resetSemFlag(n[i]) resetSemFlag(n[i])
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
flags: TExprFlags): PNode = s: PSym, flags: TExprFlags): PNode =
## Semantically check the output of a macro. ## Semantically check the output of a macro.
## This involves processes such as re-checking the macro output for type ## This involves processes such as re-checking the macro output for type
## coherence, making sure that variables declared with 'let' aren't ## coherence, making sure that variables declared with 'let' aren't
@ -363,8 +363,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
globalError(s.info, errTemplateInstantiationTooNested) globalError(s.info, errTemplateInstantiationTooNested)
c.friendModules.add(s.owner.getModule) c.friendModules.add(s.owner.getModule)
result = n result = macroResult
excl(n.flags, nfSem) excl(result.flags, nfSem)
#resetSemFlag n #resetSemFlag n
if s.typ.sons[0] == nil: if s.typ.sons[0] == nil:
result = semStmt(c, result) result = semStmt(c, result)
@ -378,13 +378,26 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
of tyStmt: of tyStmt:
result = semStmt(c, result) result = semStmt(c, result)
of tyTypeDesc: of tyTypeDesc:
if n.kind == nkStmtList: result.kind = nkStmtListType if result.kind == nkStmtList: result.kind = nkStmtListType
var typ = semTypeNode(c, result, nil) var typ = semTypeNode(c, result, nil)
result.typ = makeTypeDesc(c, typ) result.typ = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info) #result = symNodeFromType(c, typ, n.info)
else: else:
var retType = s.typ.sons[0]
if s.ast[genericParamsPos] != nil and retType.isMetaType:
# The return type may depend on the Macro arguments
# e.g. template foo(T: typedesc): seq[T]
# We will instantiate the return type here, because
# we now know the supplied arguments
var paramTypes = newIdTable()
for param, value in genericParamsInMacroCall(s, call):
idTablePut(paramTypes, param.typ, value.typ)
retType = generateTypeInstance(c, paramTypes,
macroResult.info, retType)
result = semExpr(c, result, flags) result = semExpr(c, result, flags)
result = fitNode(c, s.typ.sons[0], result, result.info) result = fitNode(c, retType, result, result.info)
#GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0])) #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
dec(evalTemplateCounter) dec(evalTemplateCounter)
discard c.friendModules.pop() discard c.friendModules.pop()
@ -409,7 +422,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
# c.evalContext = c.createEvalContext(emStatic) # c.evalContext = c.createEvalContext(emStatic)
result = evalMacroCall(c.module, c.cache, n, nOrig, sym) result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
if efNoSemCheck notin flags: if efNoSemCheck notin flags:
result = semAfterMacroCall(c, result, sym, flags) result = semAfterMacroCall(c, n, result, sym, flags)
result = wrapInComesFrom(nOrig.info, result) result = wrapInComesFrom(nOrig.info, result)
popInfoContext() popInfoContext()

View file

@ -429,7 +429,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
for i in 1..sonsLen(n)-1: for i in 1..sonsLen(n)-1:
let formal = s.ast.sons[genericParamsPos].sons[i-1].typ let formal = s.ast.sons[genericParamsPos].sons[i-1].typ
let arg = n[i].typ let arg = n[i].typ
let tm = typeRel(m, formal, arg, true) let tm = typeRel(m, formal, arg)
if tm in {isNone, isConvertible}: return nil if tm in {isNone, isConvertible}: return nil
var newInst = generateInstance(c, s, m.bindings, n.info) var newInst = generateInstance(c, s, m.bindings, n.info)
newInst.typ.flags.excl tfUnresolved newInst.typ.flags.excl tfUnresolved

View file

@ -277,6 +277,10 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
assert n != nil assert n != nil
result.n = n result.n = n
proc newTypeWithSons2*(kind: TTypeKind, owner: PSym, sons: seq[PType]): PType =
result = newType(kind, owner)
result.sons = sons
proc newTypeWithSons*(c: PContext, kind: TTypeKind, proc newTypeWithSons*(c: PContext, kind: TTypeKind,
sons: seq[PType]): PType = sons: seq[PType]): PType =
result = newType(kind, getCurrOwner(c)) result = newType(kind, getCurrOwner(c))

View file

@ -16,7 +16,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
pushInfoContext(n.info) pushInfoContext(n.info)
result = evalTemplate(n, s, getCurrOwner(c), efFromHlo in flags) result = evalTemplate(n, s, getCurrOwner(c), efFromHlo in flags)
if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
popInfoContext() popInfoContext()
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
@ -47,6 +47,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
#raiseRecoverableError("") #raiseRecoverableError("")
result = errorNode(c, n) result = errorNode(c, n)
if result.typ == nil or result.typ == enforceVoidContext: if result.typ == nil or result.typ == enforceVoidContext:
if n.kind != nkStmtList:
# we cannot check for 'void' in macros ... # we cannot check for 'void' in macros ...
localError(n.info, errExprXHasNoType, localError(n.info, errExprXHasNoType,
renderTree(result, {renderNoComments})) renderTree(result, {renderNoComments}))
@ -682,27 +683,9 @@ proc bracketedMacro(n: PNode): PSym =
if result.kind notin {skMacro, skTemplate}: if result.kind notin {skMacro, skTemplate}:
result = nil result = nil
proc semBracketedMacro(c: PContext; outer, inner: PNode; s: PSym; proc setGenericParams(c: PContext, n: PNode) =
flags: TExprFlags): PNode = for i in 1 .. <n.len:
# We received untransformed bracket expression coming from macroOrTmpl[]. n[i].typ = semTypeNode(c, n[i], nil)
# Transform it to macro or template call, where first come normal
# arguments, next come generic template arguments.
var sons = newSeq[PNode]()
sons.add inner.sons[0]
# Normal arguments:
for i in 1..<outer.len:
sons.add outer.sons[i]
# Generic template arguments from bracket expression:
for i in 1..<inner.len:
sons.add inner.sons[i]
shallowCopy(outer.sons, sons)
# FIXME: Shouldn't we check sfImmediate and call semDirectOp?
# However passing to semDirectOp doesn't work here.
case s.kind
of skMacro: result = semMacroExpr(c, outer, outer, s, flags)
of skTemplate: result = semTemplateExpr(c, outer, s, flags)
else: assert(false)
return
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
result = n result = n
@ -744,7 +727,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
elif n.sons[0].kind == nkBracketExpr: elif n.sons[0].kind == nkBracketExpr:
let s = bracketedMacro(n.sons[0]) let s = bracketedMacro(n.sons[0])
if s != nil: if s != nil:
return semBracketedMacro(c, n, n.sons[0], s, flags) setGenericParams(c, n[0])
return semDirectOp(c, n, flags)
let nOrig = n.copyTree let nOrig = n.copyTree
semOpAux(c, n) semOpAux(c, n)
@ -1155,6 +1139,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
ty = n.sons[0].typ ty = n.sons[0].typ
return nil return nil
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias}) ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias})
if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass:
ty = ty.lastSon
while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct})
var check: PNode = nil var check: PNode = nil
if ty.kind == tyObject: if ty.kind == tyObject:
@ -1713,7 +1699,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
# We transform the do block into a template with a param for # We transform the do block into a template with a param for
# each interpolation. We'll pass this template to getAst. # each interpolation. We'll pass this template to getAst.
var var
doBlk = n{-1} quotedBlock = n{-1}
op = if n.len == 3: expectString(c, n[1]) else: "``" op = if n.len == 3: expectString(c, n[1]) else: "``"
quotes = newSeq[PNode](1) quotes = newSeq[PNode](1)
# the quotes will be added to a nkCall statement # the quotes will be added to a nkCall statement
@ -1721,20 +1707,23 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
ids = newSeq[PNode]() ids = newSeq[PNode]()
# this will store the generated param names # this will store the generated param names
if doBlk.kind != nkDo: if quotedBlock.kind != nkStmtList:
localError(n.info, errXExpected, "block") localError(n.info, errXExpected, "block")
processQuotations(doBlk.sons[bodyPos], op, quotes, ids) processQuotations(quotedBlock, op, quotes, ids)
var dummyTemplate = newProcNode(
nkTemplateDef, quotedBlock.info, quotedBlock,
name = newAnonSym(c, skTemplate, n.info).newSymNode)
doBlk.sons[namePos] = newAnonSym(c, skTemplate, n.info).newSymNode
if ids.len > 0: if ids.len > 0:
doBlk.sons[paramsPos] = newNodeI(nkFormalParams, n.info) dummyTemplate.sons[paramsPos] = newNodeI(nkFormalParams, n.info)
doBlk[paramsPos].add getSysSym("typed").newSymNode # return type dummyTemplate[paramsPos].add getSysSym("typed").newSymNode # return type
ids.add getSysSym("untyped").newSymNode # params type ids.add getSysSym("untyped").newSymNode # params type
ids.add emptyNode # no default value ids.add emptyNode # no default value
doBlk[paramsPos].add newNode(nkIdentDefs, n.info, ids) dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids)
var tmpl = semTemplateDef(c, doBlk) var tmpl = semTemplateDef(c, dummyTemplate)
quotes[0] = tmpl[namePos] quotes[0] = tmpl[namePos]
result = newNode(nkCall, n.info, @[ result = newNode(nkCall, n.info, @[
getMagicSym(mExpandToAst).newSymNode, getMagicSym(mExpandToAst).newSymNode,
@ -2158,10 +2147,6 @@ proc shouldBeBracketExpr(n: PNode): bool =
n.sons[0] = be n.sons[0] = be
return true return true
proc setGenericParams(c: PContext, n: PNode) =
for i in 1 .. <n.len:
n[i].typ = semTypeNode(c, n[i], nil)
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = n result = n
if gCmd == cmdIdeTools: suggestExpr(c, n) if gCmd == cmdIdeTools: suggestExpr(c, n)
@ -2325,8 +2310,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkCurly: result = semSetConstr(c, n) of nkCurly: result = semSetConstr(c, n)
of nkBracket: result = semArrayConstr(c, n, flags) of nkBracket: result = semArrayConstr(c, n, flags)
of nkObjConstr: result = semObjConstr(c, n, flags) of nkObjConstr: result = semObjConstr(c, n, flags)
of nkLambda: result = semLambda(c, n, flags) of nkLambdaKinds: result = semLambda(c, n, flags)
of nkDo: result = semDo(c, n, flags)
of nkDerefExpr: result = semDeref(c, n) of nkDerefExpr: result = semDeref(c, n)
of nkAddr: of nkAddr:
result = n result = n

View file

@ -121,12 +121,13 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
localError(n.info, errWrongNumberOfVariables) localError(n.info, errWrongNumberOfVariables)
return result return result
var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc}) const skippedTypesForFields = abstractVar - {tyTypeDesc} + tyUserTypeClasses
var tupleTypeA = skipTypes(call.sons[1].typ, skippedTypesForFields)
if tupleTypeA.kind notin {tyTuple, tyObject}: if tupleTypeA.kind notin {tyTuple, tyObject}:
localError(n.info, errGenerated, "no object or tuple type") localError(n.info, errGenerated, "no object or tuple type")
return result return result
for i in 1..call.len-1: for i in 1..call.len-1:
var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc}) var tupleTypeB = skipTypes(call.sons[i].typ, skippedTypesForFields)
if not sameType(tupleTypeA, tupleTypeB): if not sameType(tupleTypeA, tupleTypeB):
typeMismatch(call.sons[i].info, tupleTypeA, tupleTypeB) typeMismatch(call.sons[i].info, tupleTypeA, tupleTypeB)

View file

@ -36,7 +36,8 @@ proc rawPushProcCon(c: PContext, owner: PSym) =
c.p = x c.p = x
proc rawHandleSelf(c: PContext; owner: PSym) = proc rawHandleSelf(c: PContext; owner: PSym) =
if c.selfName != nil and owner.kind in {skProc, skMethod, skConverter, skIterator, skMacro} and owner.typ != nil: const callableSymbols = {skProc, skMethod, skConverter, skIterator, skMacro}
if c.selfName != nil and owner.kind in callableSymbols and owner.typ != nil:
let params = owner.typ.n let params = owner.typ.n
if params.len > 1: if params.len > 1:
let arg = params[1].sym let arg = params[1].sym

View file

@ -109,27 +109,43 @@ proc uninstantiate(t: PType): PType =
of tyCompositeTypeClass: uninstantiate t.sons[1] of tyCompositeTypeClass: uninstantiate t.sons[1]
else: t else: t
proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode = proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
var typ = operand.skipTypes({tyTypeDesc}) const skippedTypes = {tyTypeDesc}
let trait = traitCall[0]
internalAssert trait.kind == nkSym
var operand = operand.skipTypes(skippedTypes)
template operand2: PType =
traitCall.sons[2].typ.skipTypes({tyTypeDesc})
template typeWithSonsResult(kind, sons): PNode =
newTypeWithSons2(kind, context, sons).toNode(traitCall.info)
case trait.sym.name.s case trait.sym.name.s
of "or", "|":
return typeWithSonsResult(tyOr, @[operand, operand2])
of "and":
return typeWithSonsResult(tyAnd, @[operand, operand2])
of "not":
return typeWithSonsResult(tyNot, @[operand])
of "name": of "name":
result = newStrNode(nkStrLit, typ.typeToString(preferName)) result = newStrNode(nkStrLit, operand.typeToString(preferName))
result.typ = newType(tyString, context) result.typ = newType(tyString, context)
result.info = trait.info result.info = traitCall.info
of "arity": of "arity":
result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc)) result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc))
result.typ = newType(tyInt, context) result.typ = newType(tyInt, context)
result.info = trait.info result.info = traitCall.info
of "genericHead": of "genericHead":
var res = uninstantiate(typ) var res = uninstantiate(operand)
if res == typ and res.kind notin tyMagicGenerics: if res == operand and res.kind notin tyMagicGenerics:
localError(trait.info, localError(traitCall.info,
"genericHead expects a generic type. The given type was " & "genericHead expects a generic type. The given type was " &
typeToString(typ)) typeToString(operand))
return newType(tyError, context).toNode(trait.info) return newType(tyError, context).toNode(traitCall.info)
result = res.base.toNode(trait.info) result = res.base.toNode(traitCall.info)
of "stripGenericParams": of "stripGenericParams":
result = uninstantiate(typ).toNode(trait.info) result = uninstantiate(operand).toNode(traitCall.info)
else: else:
internalAssert false internalAssert false
@ -140,7 +156,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
if t.sonsLen > 0: if t.sonsLen > 0:
# This is either a type known to sem or a typedesc # This is either a type known to sem or a typedesc
# param to a regular proc (again, known at instantiation) # param to a regular proc (again, known at instantiation)
result = evalTypeTrait(n[0], t, getCurrOwner(c)) result = evalTypeTrait(n, t, getCurrOwner(c))
else: else:
# a typedesc variable, pass unmodified to evals # a typedesc variable, pass unmodified to evals
result = n result = n

View file

@ -730,7 +730,9 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
var s: PSym var s: PSym
if name.kind == nkDotExpr: if name.kind == nkDotExpr:
s = qualifiedLookUp(c, name, {checkUndeclared, checkModule}) s = qualifiedLookUp(c, name, {checkUndeclared, checkModule})
if s.kind != skType or s.typ.skipTypes(abstractPtrs).kind != tyObject or tfPartial notin s.typ.skipTypes(abstractPtrs).flags: if s.kind != skType or
s.typ.skipTypes(abstractPtrs).kind != tyObject or
tfPartial notin s.typ.skipTypes(abstractPtrs).flags:
localError(name.info, "only .partial objects can be extended") localError(name.info, "only .partial objects can be extended")
else: else:
s = semIdentDef(c, name, skType) s = semIdentDef(c, name, skType)
@ -742,6 +744,87 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
if sfGenSym notin s.flags: addInterfaceDecl(c, s) if sfGenSym notin s.flags: addInterfaceDecl(c, s)
a.sons[0] = newSymNode(s) a.sons[0] = newSymNode(s)
proc checkCovariantParamsUsages(genericType: PType) =
var body = genericType{-1}
proc traverseSubTypes(t: PType): bool =
template error(msg) = localError(genericType.sym.info, msg)
result = false
template subresult(r) =
let sub = r
result = result or sub
case t.kind
of tyGenericParam:
t.flags.incl tfWeakCovariant
return true
of tyObject:
for field in t.n:
subresult traverseSubTypes(field.typ)
of tyArray:
return traverseSubTypes(t[1])
of tyProc:
for subType in t.sons:
if subType != nil:
subresult traverseSubTypes(subType)
if result:
error("non-invariant type param used in a proc type: " & $t)
of tySequence:
return traverseSubTypes(t[0])
of tyGenericInvocation:
let targetBody = t[0]
for i in 1 .. <t.len:
let param = t[i]
if param.kind == tyGenericParam:
if tfCovariant in param.flags:
let formalFlags = targetBody[i-1].flags
if tfCovariant notin formalFlags:
error("covariant param '" & param.sym.name.s &
"' used in a non-covariant position")
elif tfWeakCovariant in formalFlags:
param.flags.incl tfWeakCovariant
result = true
elif tfContravariant in param.flags:
let formalParam = targetBody[i-1].sym
if tfContravariant notin formalParam.typ.flags:
error("contravariant param '" & param.sym.name.s &
"' used in a non-contravariant position")
result = true
else:
subresult traverseSubTypes(param)
of tyAnd, tyOr, tyNot, tyStatic, tyBuiltInTypeClass, tyCompositeTypeClass:
error("non-invariant type parameters cannot be used with types such '" & $t & "'")
of tyUserTypeClass, tyUserTypeClassInst:
error("non-invariant type parameters are not supported in concepts")
of tyTuple:
for fieldType in t.sons:
subresult traverseSubTypes(fieldType)
of tyPtr, tyRef, tyVar:
if t.base.kind == tyGenericParam: return true
return traverseSubTypes(t.base)
of tyDistinct, tyAlias:
return traverseSubTypes(t.lastSon)
of tyGenericInst:
internalAssert false
else:
discard
discard traverseSubTypes(body)
proc typeSectionRightSidePass(c: PContext, n: PNode) = proc typeSectionRightSidePass(c: PContext, n: PNode) =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
@ -782,6 +865,22 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
body.sym = s body.sym = s
body.size = -1 # could not be computed properly body.size = -1 # could not be computed properly
s.typ.sons[sonsLen(s.typ) - 1] = body s.typ.sons[sonsLen(s.typ) - 1] = body
if tfCovariant in s.typ.flags:
checkCovariantParamsUsages(s.typ)
# XXX: This is a temporary limitation:
# The codegen currently produces various failures with
# generic imported types that have fields, but we need
# the fields specified in order to detect weak covariance.
# The proper solution is to teach the codegen how to handle
# such types, because this would offer various interesting
# possibilities such as instantiating C++ generic types with
# garbage collected Nim types.
if sfImportc in s.flags:
var body = s.typ.lastSon
if body.kind == tyObject:
# erases all declared fields
body.n.sons = nil
popOwner(c) popOwner(c)
closeScope(c) closeScope(c)
elif a.sons[2].kind != nkEmpty: elif a.sons[2].kind != nkEmpty:
@ -851,9 +950,12 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
# type aliases are hard: # type aliases are hard:
var t = semTypeNode(c, x, nil) var t = semTypeNode(c, x, nil)
assert t != nil assert t != nil
if t.kind in {tyObject, tyEnum, tyDistinct}: if s.typ != nil and s.typ.kind != tyAlias:
if t.kind in {tyProc, tyGenericInst} and not t.isMetaType:
assignType(s.typ, t)
s.typ.id = t.id
elif t.kind in {tyObject, tyEnum, tyDistinct}:
assert s.typ != nil assert s.typ != nil
if s.typ.kind != tyAlias:
assignType(s.typ, t) assignType(s.typ, t)
s.typ.id = t.id # same id s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ) checkConstructedType(s.info, s.typ)
@ -1066,13 +1168,6 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
popOwner(c) popOwner(c)
result.typ = s.typ result.typ = s.typ
proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode =
# 'do' without params produces a stmt:
if n[genericParamsPos].kind == nkEmpty and n[paramsPos].kind == nkEmpty:
result = semStmt(c, n[bodyPos])
else:
result = semLambda(c, n, flags)
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
var n = n var n = n
@ -1177,7 +1272,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
var objB = t.sons[2] var objB = t.sons[2]
while true: while true:
if objB.kind == tyGenericBody: objB = objB.lastSon if objB.kind == tyGenericBody: objB = objB.lastSon
elif objB.kind == tyGenericInvocation: objB = objB.sons[0] elif objB.kind in {tyGenericInvocation, tyGenericInst}:
objB = objB.sons[0]
else: break else: break
if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB): if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
if obj.assignment.isNil: if obj.assignment.isNil:
@ -1666,7 +1762,11 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
else: discard else: discard
if result.len == 1 and if result.len == 1 and
c.inTypeClass == 0 and # concept bodies should be preserved as a stmt list # concept bodies should be preserved as a stmt list:
c.inTypeClass == 0 and
# also, don't make life complicated for macros.
# they will always expect a proper stmtlist:
nfBlockArg notin n.flags and
result.sons[0].kind != nkDefer: result.sons[0].kind != nkDefer:
result = result.sons[0] result = result.sons[0]

View file

@ -135,7 +135,9 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
let isCall = ord(n.kind in nkCallKinds+{nkBracketExpr}) let isCall = ord(n.kind in nkCallKinds+{nkBracketExpr})
let n = if n[0].kind == nkBracket: n[0] else: n let n = if n[0].kind == nkBracket: n[0] else: n
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
var base = semTypeNode(c, n.lastSon, nil).skipTypes({tyTypeDesc}) var t = semTypeNode(c, n.lastSon, nil)
if t.kind == tyTypeDesc and tfUnresolved notin t.flags:
t = t.base
result = newOrPrevType(kind, prev, c) result = newOrPrevType(kind, prev, c)
var isNilable = false var isNilable = false
# check every except the last is an object: # check every except the last is an object:
@ -149,7 +151,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
tyError, tyObject}: tyError, tyObject}:
message n[i].info, errGenerated, "region needs to be an object type" message n[i].info, errGenerated, "region needs to be an object type"
addSonSkipIntLit(result, region) addSonSkipIntLit(result, region)
addSonSkipIntLit(result, base) addSonSkipIntLit(result, t)
#if not isNilable: result.flags.incl tfNotNil #if not isNilable: result.flags.incl tfNotNil
proc semVarType(c: PContext, n: PNode, prev: PType): PType = proc semVarType(c: PContext, n: PNode, prev: PType): PType =
@ -891,13 +893,20 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
let lifted = liftingWalk(paramType.sons[i]) let lifted = liftingWalk(paramType.sons[i])
if lifted != nil: paramType.sons[i] = lifted if lifted != nil: paramType.sons[i] = lifted
if paramType.base.lastSon.kind == tyUserTypeClass: let body = paramType.base
if body.kind == tyForward:
# this may happen for proc type appearing in a type section
# before one of its param types
return
if body.lastSon.kind == tyUserTypeClass:
let expanded = instGenericContainer(c, info, paramType, let expanded = instGenericContainer(c, info, paramType,
allowMetaTypes = true) allowMetaTypes = true)
result = liftingWalk(expanded, true) result = liftingWalk(expanded, true)
of tyUserTypeClasses, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: of tyUserTypeClasses, tyBuiltInTypeClass, tyCompositeTypeClass,
result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), true)) tyAnd, tyOr, tyNot:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), false))
of tyGenericParam: of tyGenericParam:
markUsed(info, paramType.sym, c.graph.usageSym) markUsed(info, paramType.sym, c.graph.usageSym)
@ -1065,6 +1074,7 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
proc semGenericParamInInvocation(c: PContext, n: PNode): PType = proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
result = semTypeNode(c, n, nil) result = semTypeNode(c, n, nil)
n.typ = makeTypeDesc(c, result)
proc semObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType) = proc semObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType) =
var var
@ -1574,10 +1584,24 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
# type for each generic param. the index # type for each generic param. the index
# of the parameter will be stored in the # of the parameter will be stored in the
# attached symbol. # attached symbol.
var paramName = a.sons[j]
var covarianceFlag = tfUnresolved
if paramName.safeLen == 2:
if not nimEnableCovariance or paramName[0].ident.s == "in":
if father == nil or sfImportc notin father.sym.flags:
localError(paramName.info, errInOutFlagNotExtern, paramName[0].ident.s)
covarianceFlag = if paramName[0].ident.s == "in": tfContravariant
else: tfCovariant
if father != nil: father.flags.incl tfCovariant
paramName = paramName[1]
var s = if finalType.kind == tyStatic or tfWildcard in typ.flags: var s = if finalType.kind == tyStatic or tfWildcard in typ.flags:
newSymG(skGenericParam, a.sons[j], c).linkTo(finalType) newSymG(skGenericParam, paramName, c).linkTo(finalType)
else: else:
newSymG(skType, a.sons[j], c).linkTo(finalType) newSymG(skType, paramName, c).linkTo(finalType)
if covarianceFlag != tfUnresolved: s.typ.flags.incl(covarianceFlag)
if def.kind != nkEmpty: s.ast = def if def.kind != nkEmpty: s.ast = def
if father != nil: addSonSkipIntLit(father, s.typ) if father != nil: addSonSkipIntLit(father, s.typ)
s.position = result.len s.position = result.len

View file

@ -9,10 +9,10 @@
# This module does the instantiation of generic types. # This module does the instantiation of generic types.
import ast, astalgo, msgs, types, magicsys, semdata, renderer import ast, astalgo, msgs, types, magicsys, semdata, renderer, options
const const
tfInstClearedFlags = {tfHasMeta} tfInstClearedFlags = {tfHasMeta, tfUnresolved}
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:
@ -50,6 +50,9 @@ proc searchInstTypes*(key: PType): PType =
# types such as Channel[empty]. Why? # types such as Channel[empty]. Why?
# See the notes for PActor in handleGenericInvocation # See the notes for PActor in handleGenericInvocation
return return
if not sameFlags(inst, key):
continue
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?
@ -231,6 +234,7 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
proc instCopyType*(cl: var TReplTypeVars, t: PType): PType = proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
# XXX: relying on allowMetaTypes is a kludge # XXX: relying on allowMetaTypes is a kludge
result = copyType(t, t.owner, cl.allowMetaTypes) result = copyType(t, t.owner, cl.allowMetaTypes)
if cl.allowMetaTypes: return
result.flags.incl tfFromGeneric result.flags.incl tfFromGeneric
if not (t.kind in tyMetaTypes or if not (t.kind in tyMetaTypes or
(t.kind == tyStatic and t.n == nil)): (t.kind == tyStatic and t.n == nil)):
@ -247,10 +251,11 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
result = PType(idTableGet(cl.localCache, t)) result = PType(idTableGet(cl.localCache, t))
else: else:
result = searchInstTypes(t) result = searchInstTypes(t)
if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: 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 in {tyGenericParam}:
x = lookupTypeVar(cl, x) x = lookupTypeVar(cl, x)
if x != nil: if x != nil:
if header == t: header = instCopyType(cl, t) if header == t: header = instCopyType(cl, t)
@ -307,6 +312,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
rawAddSon(result, newbody) rawAddSon(result, newbody)
checkPartialConstructedType(cl.info, newbody) checkPartialConstructedType(cl.info, newbody)
let dc = newbody.deepCopy let dc = newbody.deepCopy
if cl.allowMetaTypes == false:
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
# 'deepCopy' needs to be instantiated for # 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*: # generics *when the type is constructed*:
@ -526,6 +532,14 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
result = replaceTypeVarsT(cl, t) result = replaceTypeVarsT(cl, t)
popInfoContext() popInfoContext()
proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo,
t: PType): PType =
var cl = initTypeVars(p, pt, info, nil)
cl.allowMetaTypes = true
pushInfoContext(info)
result = replaceTypeVarsT(cl, t)
popInfoContext()
template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode, template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode,
t: PType): untyped = t: PType): untyped =
generateTypeInstance(p, pt, arg.info, t) generateTypeInstance(p, pt, arg.info, t)

View file

@ -154,7 +154,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
else: else:
c.hashSym(t.sym) c.hashSym(t.sym)
return return
of tyAlias, tyGenericInst: of tyAlias, tyGenericInst, tyUserTypeClasses:
c.hashType t.lastSon, flags c.hashType t.lastSon, flags
return return
else: else:
@ -201,16 +201,6 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
of tyRef, tyPtr, tyGenericBody, tyVar: of tyRef, tyPtr, tyGenericBody, tyVar:
c.hashType t.lastSon, flags c.hashType t.lastSon, flags
if tfVarIsPtr in t.flags: c &= ".varisptr" if tfVarIsPtr in t.flags: c &= ".varisptr"
of tyUserTypeClass:
if t.sym != nil and t.sym.owner != nil:
c &= t.sym.owner.name.s
else:
c &= "unknown typeclass"
of tyUserTypeClassInst:
let body = t.sons[0]
c.hashSym body.sym
for i in countup(1, sonsLen(t) - 2):
c.hashType t.sons[i], flags
of tyFromExpr, tyFieldAccessor: of tyFromExpr, tyFieldAccessor:
c.hashTree(t.n) c.hashTree(t.n)
of tyTuple: of tyTuple:

View file

@ -69,6 +69,12 @@ type
mutabilityProblem*: uint8 # tyVar mismatch mutabilityProblem*: uint8 # tyVar mismatch
inheritancePenalty: int # to prefer closest father object type inheritancePenalty: int # to prefer closest father object type
TTypeRelFlag* = enum
trDontBind
trNoCovariance
TTypeRelFlags* = set[TTypeRelFlag]
TTypeRelation* = enum # order is important! TTypeRelation* = enum # order is important!
isNone, isConvertible, isNone, isConvertible,
isIntConv, isIntConv,
@ -177,6 +183,13 @@ proc sumGeneric(t: PType): int =
tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody: tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody:
t = t.lastSon t = t.lastSon
inc result inc result
of tyOr:
var maxBranch = 0
for branch in t.sons:
let branchSum = branch.sumGeneric
if branchSum > maxBranch: maxBranch = branchSum
inc result, maxBranch + 1
break
of tyVar: of tyVar:
t = t.sons[0] t = t.sons[0]
inc result inc result
@ -185,8 +198,8 @@ proc sumGeneric(t: PType): int =
t = t.lastSon t = t.lastSon
if t.kind == tyEmpty: break if t.kind == tyEmpty: break
inc result inc result
of tyGenericInvocation, tyTuple, tyProc: of tyGenericInvocation, tyTuple, tyProc, tyAnd:
result += ord(t.kind == tyGenericInvocation) result += ord(t.kind in {tyGenericInvocation, tyAnd})
for i in 0 .. <t.len: for i in 0 .. <t.len:
if t.sons[i] != nil: if t.sons[i] != nil:
result += t.sons[i].sumGeneric result += t.sons[i].sumGeneric
@ -228,6 +241,15 @@ proc complexDisambiguation(a, b: PType): int =
for i in 1 .. <b.len: y += b.sons[i].sumGeneric for i in 1 .. <b.len: y += b.sons[i].sumGeneric
result = x - y result = x - y
proc writeMatches*(c: TCandidate) =
echo "Candidate '", c.calleeSym.name.s, "' at ", c.calleeSym.info
echo " exact matches: ", c.exactMatches
echo " generic matches: ", c.genericMatches
echo " subtype matches: ", c.subtypeMatches
echo " intconv matches: ", c.intConvMatches
echo " conv matches: ", c.convMatches
echo " inheritance: ", c.inheritancePenalty
proc cmpCandidates*(a, b: TCandidate): int = proc cmpCandidates*(a, b: TCandidate): int =
result = a.exactMatches - b.exactMatches result = a.exactMatches - b.exactMatches
if result != 0: return if result != 0: return
@ -248,14 +270,6 @@ proc cmpCandidates*(a, b: TCandidate): int =
if result != 0: return if result != 0: return
result = a.calleeScope - b.calleeScope result = a.calleeScope - b.calleeScope
proc writeMatches*(c: TCandidate) =
writeLine(stdout, "exact matches: " & $c.exactMatches)
writeLine(stdout, "generic matches: " & $c.genericMatches)
writeLine(stdout, "subtype matches: " & $c.subtypeMatches)
writeLine(stdout, "intconv matches: " & $c.intConvMatches)
writeLine(stdout, "conv matches: " & $c.convMatches)
writeLine(stdout, "inheritance: " & $c.inheritancePenalty)
proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string = proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
if arg.kind in nkSymChoices: if arg.kind in nkSymChoices:
result = typeToString(arg[0].typ, prefer) result = typeToString(arg[0].typ, prefer)
@ -288,7 +302,9 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
add(result, argTypeToString(arg, prefer)) add(result, argTypeToString(arg, prefer))
if i != sonsLen(n) - 1: add(result, ", ") if i != sonsLen(n) - 1: add(result, ", ")
proc typeRel*(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation proc typeRel*(c: var TCandidate, f, aOrig: PType,
flags: TTypeRelFlags = {}): TTypeRelation
proc concreteType(c: TCandidate, t: PType): PType = proc concreteType(c: TCandidate, t: PType): PType =
case t.kind case t.kind
of tyNil: of tyNil:
@ -739,7 +755,7 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
allowMetaTypes = allowUnresolved) allowMetaTypes = allowUnresolved)
result = c.c.semExpr(c.c, instantiated) result = c.c.semExpr(c.c, instantiated)
proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType = proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
# This is a simple integer arithimetic equation solver, # This is a simple integer arithimetic equation solver,
# capable of deriving the value of a static parameter in # capable of deriving the value of a static parameter in
# expressions such as (N + 5) / 2 = rhs # expressions such as (N + 5) / 2 = rhs
@ -754,64 +770,69 @@ proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType =
# #
# Result: # Result:
# #
# The proc will return the inferred static type with the `n` field # The proc will return true if the static types was successfully
# populated with the inferred value. # inferred. The result will be bound to the original static type
# # in the TCandidate.
# `nil` will be returned if the inference was not possible
# #
if lhs.kind in nkCallKinds and lhs[0].kind == nkSym: if lhs.kind in nkCallKinds and lhs[0].kind == nkSym:
case lhs[0].sym.magic case lhs[0].sym.magic
of mUnaryLt: of mUnaryLt:
return inferStaticParam(lhs[1], rhs + 1) return inferStaticParam(c, lhs[1], rhs + 1)
of mAddI, mAddU, mInc, mSucc: of mAddI, mAddU, mInc, mSucc:
if lhs[1].kind == nkIntLit: if lhs[1].kind == nkIntLit:
return inferStaticParam(lhs[2], rhs - lhs[1].intVal) return inferStaticParam(c, lhs[2], rhs - lhs[1].intVal)
elif lhs[2].kind == nkIntLit: elif lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs - lhs[2].intVal) return inferStaticParam(c, lhs[1], rhs - lhs[2].intVal)
of mDec, mSubI, mSubU, mPred: of mDec, mSubI, mSubU, mPred:
if lhs[1].kind == nkIntLit: if lhs[1].kind == nkIntLit:
return inferStaticParam(lhs[2], lhs[1].intVal - rhs) return inferStaticParam(c, lhs[2], lhs[1].intVal - rhs)
elif lhs[2].kind == nkIntLit: elif lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs + lhs[2].intVal) return inferStaticParam(c, lhs[1], rhs + lhs[2].intVal)
of mMulI, mMulU: of mMulI, mMulU:
if lhs[1].kind == nkIntLit: if lhs[1].kind == nkIntLit:
if rhs mod lhs[1].intVal == 0: if rhs mod lhs[1].intVal == 0:
return inferStaticParam(lhs[2], rhs div lhs[1].intVal) return inferStaticParam(c, lhs[2], rhs div lhs[1].intVal)
elif lhs[2].kind == nkIntLit: elif lhs[2].kind == nkIntLit:
if rhs mod lhs[2].intVal == 0: if rhs mod lhs[2].intVal == 0:
return inferStaticParam(lhs[1], rhs div lhs[2].intVal) return inferStaticParam(c, lhs[1], rhs div lhs[2].intVal)
of mDivI, mDivU: of mDivI, mDivU:
if lhs[1].kind == nkIntLit: if lhs[1].kind == nkIntLit:
if lhs[1].intVal mod rhs == 0: if lhs[1].intVal mod rhs == 0:
return inferStaticParam(lhs[2], lhs[1].intVal div rhs) return inferStaticParam(c, lhs[2], lhs[1].intVal div rhs)
elif lhs[2].kind == nkIntLit: elif lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], lhs[2].intVal * rhs) return inferStaticParam(c, lhs[1], lhs[2].intVal * rhs)
of mShlI: of mShlI:
if lhs[2].kind == nkIntLit: if lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs shr lhs[2].intVal) return inferStaticParam(c, lhs[1], rhs shr lhs[2].intVal)
of mShrI: of mShrI:
if lhs[2].kind == nkIntLit: if lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs shl lhs[2].intVal) return inferStaticParam(c, lhs[1], rhs shl lhs[2].intVal)
of mUnaryMinusI: of mUnaryMinusI:
return inferStaticParam(lhs[1], -rhs) return inferStaticParam(c, lhs[1], -rhs)
of mUnaryPlusI, mToInt, mToBiggestInt: of mUnaryPlusI, mToInt, mToBiggestInt:
return inferStaticParam(lhs[1], rhs) return inferStaticParam(c, lhs[1], rhs)
else: discard else: discard
elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil: elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil:
lhs.typ.n = newIntNode(nkIntLit, rhs) var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ.sons)
return lhs.typ inferred.n = newIntNode(nkIntLit, rhs)
put(c, lhs.typ, inferred)
if c.c.inTypeClass > 0:
# inside concepts, binding is currently done with
# direct mutation of the involved types:
lhs.typ.n = inferred.n
return true
return nil return false
proc failureToInferStaticParam(n: PNode) = proc failureToInferStaticParam(n: PNode) =
let staticParam = n.findUnresolvedStatic let staticParam = n.findUnresolvedStatic
@ -825,13 +846,10 @@ proc inferStaticsInRange(c: var TCandidate,
allowUnresolved = true) allowUnresolved = true)
let upperBound = tryResolvingStaticExpr(c, inferred.n[1], let upperBound = tryResolvingStaticExpr(c, inferred.n[1],
allowUnresolved = true) allowUnresolved = true)
template doInferStatic(e: PNode, r: BiggestInt) =
template doInferStatic(c: var TCandidate, e: PNode, r: BiggestInt) =
var exp = e var exp = e
var rhs = r var rhs = r
var inferred = inferStaticParam(exp, rhs) if inferStaticParam(c, exp, rhs):
if inferred != nil:
put(c, inferred, inferred)
return isGeneric return isGeneric
else: else:
failureToInferStaticParam exp failureToInferStaticParam exp
@ -842,15 +860,36 @@ proc inferStaticsInRange(c: var TCandidate,
return isGeneric return isGeneric
else: else:
return isNone return isNone
doInferStatic(c, upperBound, lengthOrd(concrete) + lowerBound.intVal - 1) doInferStatic(upperBound, lengthOrd(concrete) + lowerBound.intVal - 1)
elif upperBound.kind == nkIntLit: elif upperBound.kind == nkIntLit:
doInferStatic(c, lowerBound, upperBound.intVal + 1 - lengthOrd(concrete)) doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(concrete))
template subtypeCheck() = template subtypeCheck() =
if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}: if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}:
result = isNone result = isNone
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
# this proc is always called for a pair of matching types
assert f.kind == a.kind
template baseTypesCheck(lhs, rhs: PType): bool =
lhs.kind notin {tyPtr, tyRef, tyVar} and
typeRel(c, lhs, rhs, {trNoCovariance}) == isSubtype
case f.kind
of tyRef, tyPtr:
return baseTypesCheck(f.base, a.base)
of tyGenericInst:
let body = f.base
return body == a.base and
a.sonsLen == 3 and
tfWeakCovariant notin body.sons[0].flags and
baseTypesCheck(f.sons[1], a.sons[1])
else:
return false
proc typeRel(c: var TCandidate, f, aOrig: PType,
flags: TTypeRelFlags = {}): 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
@ -917,6 +956,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isEqual result = isEqual
return return
template doBind: bool = trDontBind notin flags
# var and static arguments match regular modifier-free types # var and static arguments match regular modifier-free types
var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym) var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym)
# XXX: Theoretically, maybeSkipDistinct could be called before we even # XXX: Theoretically, maybeSkipDistinct could be called before we even
@ -946,23 +987,28 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
case a.kind case a.kind
of tyOr: of tyOr:
# XXX: deal with the current dual meaning of tyGenericParam
c.typedescMatched = true
# seq[int|string] vs seq[number] # seq[int|string] vs seq[number]
# both int and string must match against number # both int and string must match against number
# but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219): # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219):
result = isGeneric result = isGeneric
for branch in a.sons: for branch in a.sons:
let x = typeRel(c, f, branch, false) let x = typeRel(c, f, branch, flags + {trDontBind})
if x == isNone: return isNone if x == isNone: return isNone
if x < result: result = x if x < result: result = x
return
of tyAnd: of tyAnd:
# XXX: deal with the current dual meaning of tyGenericParam
c.typedescMatched = true
# seq[Sortable and Iterable] vs seq[Sortable] # seq[Sortable and Iterable] vs seq[Sortable]
# only one match is enough # only one match is enough
for branch in a.sons: for branch in a.sons:
let x = typeRel(c, f, branch, false) let x = typeRel(c, f, branch, flags + {trDontBind})
if x != isNone: if x != isNone:
return if x >= isGeneric: isGeneric else: x return if x >= isGeneric: isGeneric else: x
result = isNone return isNone
of tyNot: of tyNot:
case f.kind case f.kind
@ -986,7 +1032,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyUserTypeClass, tyUserTypeClassInst: of tyUserTypeClass, tyUserTypeClassInst:
# consider this: 'var g: Node' *within* a concept where 'Node' # consider this: 'var g: Node' *within* a concept where 'Node'
# is a concept too (tgraph) # is a concept too (tgraph)
let x = typeRel(c, a, f, false) let x = typeRel(c, a, f, flags + {trDontBind})
if x >= isGeneric: if x >= isGeneric:
return isGeneric return isGeneric
else: discard else: discard
@ -1032,7 +1078,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyFloat128: result = handleFloatRange(f, a) of tyFloat128: result = handleFloatRange(f, a)
of tyVar: of tyVar:
if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base) if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base)
else: result = typeRel(c, f.base, aOrig) else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
subtypeCheck() subtypeCheck()
of tyArray: of tyArray:
case a.kind case a.kind
@ -1046,38 +1092,60 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
fRange = a fRange = a
else: else:
fRange = prev fRange = prev
result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}), let ff = f.sons[1].skipTypes({tyTypeDesc})
a.sons[1].skipTypes({tyTypeDesc})) let aa = a.sons[1].skipTypes({tyTypeDesc})
if result < isGeneric: return isNone result = typeRel(c, ff, aa)
if result < isGeneric:
if nimEnableCovariance and
trNoCovariance notin flags and
ff.kind == aa.kind and
isCovariantPtr(c, ff, aa):
result = isSubtype
else:
return isNone
if fRange.rangeHasUnresolvedStatic: if fRange.rangeHasUnresolvedStatic:
return inferStaticsInRange(c, fRange, a) return inferStaticsInRange(c, fRange, a)
elif c.c.inTypeClass > 0 and aRange.rangeHasUnresolvedStatic: elif c.c.inTypeClass > 0 and aRange.rangeHasUnresolvedStatic:
return inferStaticsInRange(c, aRange, f) return inferStaticsInRange(c, aRange, f)
elif lengthOrd(fRange) != lengthOrd(a): else:
if lengthOrd(fRange) != lengthOrd(aRange):
result = isNone result = isNone
else: discard else: discard
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
# varargs[expr] is special too but handled earlier. So we only need to # varargs[expr] is special too but handled earlier. So we only need to
# handle varargs[stmt] which is the same as varargs[typed]: # handle varargs[stmt] which is the same as varargs[typed]:
if f.kind == tyVarargs: if f.kind == tyVarargs:
if tfVarargs in a.flags:
return typeRel(c, f.base, a.lastSon)
if tfOldSchoolExprStmt in f.sons[0].flags: if tfOldSchoolExprStmt in f.sons[0].flags:
if f.sons[0].kind == tyExpr: return if f.sons[0].kind == tyExpr: return
elif f.sons[0].kind == tyStmt: return elif f.sons[0].kind == tyStmt: return
template matchArrayOrSeq(aBase: PType) =
let ff = f.base
let aa = aBase
let baseRel = typeRel(c, ff, aa)
if baseRel >= isGeneric:
result = isConvertible
elif nimEnableCovariance and
trNoCovariance notin flags and
ff.kind == aa.kind and
isCovariantPtr(c, ff, aa):
result = isConvertible
case a.kind case a.kind
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = typeRel(c, base(f), base(a)) result = typeRel(c, base(f), base(a))
if result < isGeneric: result = isNone if result < isGeneric: result = isNone
of tyArray: of tyArray:
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
result = isSubtype return isSubtype
elif typeRel(c, base(f), a.sons[1]) >= isGeneric: matchArrayOrSeq(a.sons[1])
result = isConvertible
of tySequence: of tySequence:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
result = isConvertible return isConvertible
elif typeRel(c, base(f), a.sons[0]) >= isGeneric: matchArrayOrSeq(a.sons[0])
result = isConvertible
of tyString: of tyString:
if f.kind == tyOpenArray: if f.kind == tyOpenArray:
if f.sons[0].kind == tyChar: if f.sons[0].kind == tyChar:
@ -1092,8 +1160,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
result = isSubtype result = isSubtype
else: else:
result = typeRel(c, f.sons[0], a.sons[0]) let ff = f.sons[0]
if result < isGeneric: result = isNone let aa = a.sons[0]
result = typeRel(c, ff, aa)
if result < isGeneric:
if nimEnableCovariance and
trNoCovariance notin flags and
ff.kind == aa.kind and
isCovariantPtr(c, ff, aa):
result = isSubtype
else:
result = isNone
elif tfNotNil in f.flags and tfNotNil notin a.flags: elif tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion result = isNilConversion
of tyNil: result = f.allowsNil of tyNil: result = f.allowsNil
@ -1145,7 +1222,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
if a.len < f.len: return isNone if a.len < f.len: return isNone
for i in 0..f.len-2: for i in 0..f.len-2:
if typeRel(c, f.sons[i], a.sons[i]) == isNone: return isNone if typeRel(c, f.sons[i], a.sons[i]) == isNone: return isNone
result = typeRel(c, f.lastSon, a.lastSon) result = typeRel(c, f.lastSon, a.lastSon, flags + {trNoCovariance})
subtypeCheck() subtypeCheck()
if result <= isConvertible: result = isNone if result <= isConvertible: result = isNone
elif tfNotNil in f.flags and tfNotNil notin a.flags: elif tfNotNil in f.flags and tfNotNil notin a.flags:
@ -1204,9 +1281,66 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyEmpty, tyVoid: of tyEmpty, tyVoid:
if a.kind == f.kind: result = isEqual if a.kind == f.kind: result = isEqual
of tyGenericInst, tyAlias: of tyAlias:
result = typeRel(c, lastSon(f), a) result = typeRel(c, lastSon(f), a)
of tyGenericInst:
var prev = PType(idTableGet(c.bindings, f))
var f = if prev == nil: f else: prev
let roota = a.skipGenericAlias
let rootf = f.skipGenericAlias
var m = c
if a.kind == tyGenericInst:
if roota.base == rootf.base:
let nextFlags = flags + {trNoCovariance}
var hasCovariance = false
for i in 1 .. rootf.sonsLen-2:
let ff = rootf.sons[i]
let aa = roota.sons[i]
result = typeRel(c, ff, aa, nextFlags)
if result notin {isEqual, isGeneric}:
if trNoCovariance notin flags and ff.kind == aa.kind:
let paramFlags = rootf.base.sons[i-1].flags
hasCovariance =
if tfCovariant in paramFlags:
if tfWeakCovariant in paramFlags:
isCovariantPtr(c, ff, aa)
else:
ff.kind notin {tyRef, tyPtr} and result == isSubtype
else:
tfContravariant in paramFlags and
typeRel(c, aa, ff) == isSubtype
if hasCovariance:
continue
return isNone
if prev == nil: put(c, f, a)
result = isGeneric
else:
let fKind = rootf.lastSon.kind
if fKind in {tyAnd, tyOr}:
result = typeRel(c, lastSon(f), a)
if result != isNone: put(c, f, a)
return
var aAsObject = roota.lastSon
if fKind in {tyRef, tyPtr} and aAsObject.kind == fKind:
aAsObject = aAsObject.base
if aAsObject.kind == tyObject:
let baseType = aAsObject.base
if baseType != nil:
c.inheritancePenalty += 1
return typeRel(c, f, baseType)
result = isNone
else:
result = typeRel(c, lastSon(f), a)
if result != isNone: put(c, f, a)
of tyGenericBody: of tyGenericBody:
considerPreviousT: considerPreviousT:
if a.kind == tyGenericInst and a.sons[0] == f: if a.kind == tyGenericInst and a.sons[0] == f:
@ -1217,6 +1351,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyGenericInvocation: of tyGenericInvocation:
var x = a.skipGenericAlias var x = a.skipGenericAlias
# XXX: This is very hacky. It should be moved back into liftTypeParam
if x.kind in {tyGenericInst, tyArray} and
c.calleeSym != nil and
c.calleeSym.kind == skProc:
let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
return typeRel(c, inst, a)
var depth = 0 var depth = 0
if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody: if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody:
#InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation") #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation")
@ -1333,10 +1474,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
if f.isResolvedUserTypeClass: if f.isResolvedUserTypeClass:
result = typeRel(c, f.lastSon, a) result = typeRel(c, f.lastSon, a)
else: else:
considerPreviousT:
var matched = matchUserTypeClass(c.c, c, f, aOrig) var matched = matchUserTypeClass(c.c, c, f, aOrig)
if matched != nil: if matched != nil:
bindConcreteTypeToUserTypeClass(matched, a) bindConcreteTypeToUserTypeClass(matched, a)
put(c, f, matched) if doBind: put(c, f, matched)
result = isGeneric result = isGeneric
else: else:
result = isNone result = isNone
@ -1357,6 +1499,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
if result != isNone: if result != isNone:
put(c, f, a) put(c, f, a)
result = isGeneric result = isGeneric
of tyGenericParam: of tyGenericParam:
var x = PType(idTableGet(c.bindings, f)) var x = PType(idTableGet(c.bindings, f))
if x == nil: if x == nil:
@ -1376,16 +1519,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
internalAssert a.sons != nil and a.sons.len > 0 internalAssert a.sons != nil and a.sons.len > 0
c.typedescMatched = true c.typedescMatched = true
var aa = a var aa = a
while aa.kind in {tyTypeDesc, tyGenericParam} and while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0:
aa.len > 0:
aa = lastSon(aa) aa = lastSon(aa)
if aa.kind == tyGenericParam:
return isGeneric
result = typeRel(c, f.base, aa) result = typeRel(c, f.base, aa)
if result > isGeneric: result = isGeneric if result > isGeneric: result = isGeneric
else: else:
result = isNone result = isNone
else: else:
if f.sonsLen > 0 and f.sons[0].kind != tyNone: if f.sonsLen > 0 and f.sons[0].kind != tyNone:
result = typeRel(c, f.lastSon, a) result = typeRel(c, f.lastSon, a, flags + {trDontBind})
if doBind and result notin {isNone, isGeneric}: if doBind and result notin {isNone, isGeneric}:
let concrete = concreteType(c, a) let concrete = concreteType(c, a)
if concrete == nil: return isNone if concrete == nil: return isNone
@ -1590,6 +1734,10 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
of isEqual: inc(m.exactMatches) of isEqual: inc(m.exactMatches)
of isNone: discard of isNone: discard
template matchesVoidProc(t: PType): bool =
(t.kind == tyProc and t.len == 1 and t.sons[0] == nil) or
(t.kind == tyBuiltInTypeClass and t.sons[0].kind == tyProc)
proc paramTypesMatchAux(m: var TCandidate, f, a: PType, proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
argSemantized, argOrig: PNode): PNode = argSemantized, argOrig: PNode): PNode =
var var
@ -1726,6 +1874,14 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
inc(m.genericMatches) inc(m.genericMatches)
m.fauxMatch = a.kind m.fauxMatch = a.kind
return arg return arg
elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
# lift do blocks without params to lambdas
let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, argOrig), {})
if f.kind == tyBuiltInTypeClass:
inc m.genericMatches
put(m, f, lifted.typ)
inc m.convMatches
return implicitConv(nkHiddenStdConv, f, lifted, m, c)
result = userConvMatch(c, m, f, a, arg) result = userConvMatch(c, m, f, a, arg)
# check for a base type match, which supports varargs[T] without [] # check for a base type match, which supports varargs[T] without []
# constructor in a call: # constructor in a call:
@ -2008,6 +2164,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
#assert(container == nil) #assert(container == nil)
if container.isNil: if container.isNil:
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
container.typ.flags.incl tfVarargs
else: else:
incrIndexType(container.typ) incrIndexType(container.typ)
addSon(container, arg) addSon(container, arg)
@ -2086,6 +2243,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'") localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'")
return nil return nil
var f = dc.typ.sons[col] var f = dc.typ.sons[col]
if op == attachedDeepCopy: if op == attachedDeepCopy:
if f.kind in {tyRef, tyPtr}: f = f.lastSon if f.kind in {tyRef, tyPtr}: f = f.lastSon
else: else:

View file

@ -886,6 +886,9 @@ 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 sameFlags*(a, b: PType): bool {.inline.} =
result = eqTypeFlags*a.flags == eqTypeFlags*b.flags
proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
template cycleCheck() = template cycleCheck() =
# believe it or not, the direct check for ``containsOrIncl(c, a, b)`` # believe it or not, the direct check for ``containsOrIncl(c, a, b)``
@ -898,9 +901,6 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
else: else:
if containsOrIncl(c, a, b): return true if containsOrIncl(c, a, b): return true
proc sameFlags(a, b: PType): bool {.inline.} =
result = eqTypeFlags*a.flags == eqTypeFlags*b.flags
if x == y: return true if x == y: return true
var a = skipTypes(x, {tyGenericInst, tyAlias}) var a = skipTypes(x, {tyGenericInst, tyAlias})
var b = skipTypes(y, {tyGenericInst, tyAlias}) var b = skipTypes(y, {tyGenericInst, tyAlias})

View file

@ -1606,6 +1606,13 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg =
n.typ = x.typ n.typ = x.typ
result.node = n result.node = n
iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
let gp = macroSym.ast[genericParamsPos]
for i in 0 .. <gp.len:
let genericParam = gp[i].sym
let posInCall = macroSym.typ.len + i
yield (genericParam, call[posInCall])
var evalMacroCounter: int var evalMacroCounter: int
proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,

View file

@ -111,6 +111,105 @@ relation is extended to the types ``var``, ``ref``, ``ptr``:
.. XXX nil is a special value! .. XXX nil is a special value!
Covariance
----------
Covariance in Nim can be introduced only though pointer-like types such
as ``ptr`` and ``ref``. Sequence, Array and OpenArray types, instantiated
with pointer-like types will be considered covariant if and only if they
are also immutable. The introduction of a ``var`` modifier or additional
``ptr`` or ``ref`` indirections would result in invariant treatment of
these types.
``proc`` types are currently always invariant, but future versions of Nim
may relax this rule.
User-defined generic types may also be covariant with respect to some of
their parameters. By default, all generic params are considered invariant,
but you may choose the apply the prefix modifier ``in`` to a parameter to
make it contravariant or ``out`` to make it covariant:
.. code-block:: nim
type
AnnotatedPtr[out T] =
metadata: MyTypeInfo
p: ref T
RingBuffer[out T] =
startPos: int
data: seq[T]
Action {.importcpp: "std::function<void ('0)>".} [in T] = object
When the designated generic parameter is used to instantiate a pointer-like
type as in the case of `AnnotatedPtr` above, the resulting generic type will
also have pointer-like covariance:
.. code-block:: nim
type
GuiWidget = object of RootObj
Button = object of GuiWidget
ComboBox = object of GuiWidget
var
widgetPtr: AnnotatedPtr[GuiWidget]
buttonPtr: AnnotatedPtr[Button]
...
proc drawWidget[T](x: AnnotatedPtr[GuiWidget]) = ...
# you can call procs expecting base types by supplying a derived type
drawWidget(buttonPtr)
# and you can convert more-specific pointer types to more general ones
widgetPtr = buttonPtr
Just like with regular pointers, covariance will be enabled only for immutable
values:
.. code-block:: nim
proc makeComboBox[T](x: var AnnotatedPtr[GuiWidget]) =
x.p = new(ComboBox)
makeComboBox(buttonPtr) # Error, AnnotatedPtr[Button] cannot be modified
# to point to a ComboBox
On the other hand, in the `RingBuffer` example above, the designated generic
param is used to instantiate the non-pointer ``seq`` type, which means that
the resulting generic type will have covariance that mimics an array or
sequence (i.e. it will be covariant only when instantiated with ``ptr`` and
``ref`` types):
.. code-block:: nim
type
Base = object of RootObj
Derived = object of Base
proc consumeBaseValues(b: RingBuffer[Base]) = ...
var derivedValues: RingBuffer[Derived]
consumeBaseValues(derivedValues) # Error, Base and Derived values may differ
# in size
proc consumeBasePointers(b: RingBuffer[ptr Base]) = ...
var derivedPointers: RingBuffer[ptr Derived]
consumeBaseValues(derivedPointers) # This is legal
Please note that Nim will treat the user-defined pointer-like types as
proper alternatives to the built-in pointer types. That is, types such
as `seq[AnnotatedPtr[T]]` or `RingBuffer[AnnotatedPtr[T]]` will also be
considered covariant and you can create new pointer-like types by instantiating
other user-defined pointer-like types.
The contravariant parameters introduced with the ``in`` modifier are currently
useful only when interfacing with imported types having such semantics.
Convertible relation Convertible relation
-------------------- --------------------
A type ``a`` is **implicitly** convertible to type ``b`` iff the following A type ``a`` is **implicitly** convertible to type ``b`` iff the following
@ -119,6 +218,8 @@ algorithm returns true:
.. code-block:: nim .. code-block:: nim
# XXX range types? # XXX range types?
proc isImplicitlyConvertible(a, b: PType): bool = proc isImplicitlyConvertible(a, b: PType): bool =
if isSubtype(a, b) or isCovariant(a, b):
return true
case a.kind case a.kind
of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16, of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16,
uint32, uint64, float, float32, float64} uint32, uint64, float, float32, float64}

View file

@ -78,6 +78,10 @@ type
## Statically typed wrapper around a JavaScript object. ## Statically typed wrapper around a JavaScript object.
NotString = concept c NotString = concept c
c isnot string c isnot string
js* = JsObject
var jsarguments* {.importc: "arguments", nodecl}: JsObject
## JavaScript's arguments pseudo-variable
# New # New
proc newJsObject*: JsObject {. importcpp: "{@}" .} proc newJsObject*: JsObject {. importcpp: "{@}" .}
@ -93,18 +97,64 @@ proc hasOwnProperty*(x: JsObject, prop: cstring): bool
proc jsTypeOf*(x: JsObject): cstring {. importcpp: "typeof(#)" .} proc jsTypeOf*(x: JsObject): cstring {. importcpp: "typeof(#)" .}
## Returns the name of the JsObject's JavaScript type as a cstring. ## Returns the name of the JsObject's JavaScript type as a cstring.
proc jsnew*(x: auto): JsObject {.importcpp: "(new #)".}
## Turns a regular function call into an invocation of the
## JavaScript's `new` operator
proc jsdelete*(x: auto): JsObject {.importcpp: "(delete #)".}
## JavaScript's `delete` operator
# Conversion to and from JsObject # Conversion to and from JsObject
proc to*(x: JsObject, T: typedesc): T {. importcpp: "(#)" .} proc to*(x: JsObject, T: typedesc): T {. importcpp: "(#)" .}
## Converts a JsObject `x` to type `T`. ## Converts a JsObject `x` to type `T`.
proc toJs*[T](val: T): JsObject {. importcpp: "(#)" .} proc toJs*[T](val: T): JsObject {. importcpp: "(#)" .}
## Converts a value of any type to type JsObject ## Converts a value of any type to type JsObject
template toJs*(s: string): JsObject = cstring(s).toJs
macro jsFromAst*(n: untyped): untyped =
result = n
if n.kind == nnkStmtList:
result = newProc(procType = nnkDo, body = result)
return quote: toJs(`result`)
proc `&`*(a, b: cstring): cstring {.importcpp: "(# + #)".}
## Concatenation operator for JavaScript strings
proc `+` *(x, y: JsObject): JsObject {. importcpp: "(# + #)" .}
proc `-` *(x, y: JsObject): JsObject {. importcpp: "(# - #)" .}
proc `*` *(x, y: JsObject): JsObject {. importcpp: "(# * #)" .}
proc `/` *(x, y: JsObject): JsObject {. importcpp: "(# / #)" .}
proc `%` *(x, y: JsObject): JsObject {. importcpp: "(# % #)" .}
proc `+=` *(x, y: JsObject): JsObject {. importcpp: "(# += #)", discardable .}
proc `-=` *(x, y: JsObject): JsObject {. importcpp: "(# -= #)", discardable .}
proc `*=` *(x, y: JsObject): JsObject {. importcpp: "(# *= #)", discardable .}
proc `/=` *(x, y: JsObject): JsObject {. importcpp: "(# /= #)", discardable .}
proc `%=` *(x, y: JsObject): JsObject {. importcpp: "(# %= #)", discardable .}
proc `++` *(x: JsObject): JsObject {. importcpp: "(++#)" .}
proc `--` *(x: JsObject): JsObject {. importcpp: "(--#)" .}
proc `>` *(x, y: JsObject): JsObject {. importcpp: "(# > #)" .}
proc `<` *(x, y: JsObject): JsObject {. importcpp: "(# < #)" .}
proc `>=` *(x, y: JsObject): JsObject {. importcpp: "(# >= #)" .}
proc `<=` *(x, y: JsObject): JsObject {. importcpp: "(# <= #)" .}
proc `and`*(x, y: JsObject): JsObject {. importcpp: "(# && #)" .}
proc `or` *(x, y: JsObject): JsObject {. importcpp: "(# || #)" .}
proc `not`*(x: JsObject): JsObject {. importcpp: "(!#)" .}
proc `in` *(x, y: JsObject): JsObject {. importcpp: "(# in #)" .}
proc `[]`*(obj: JsObject, field: cstring): JsObject {. importcpp: getImpl .} proc `[]`*(obj: JsObject, field: cstring): JsObject {. importcpp: getImpl .}
## Return the value of a property of name `field` from a JsObject `obj`. ## Return the value of a property of name `field` from a JsObject `obj`.
proc `[]`*(obj: JsObject, field: int): JsObject {. importcpp: getImpl .}
## Return the value of a property of name `field` from a JsObject `obj`.
proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {. importcpp: setImpl .} proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {. importcpp: setImpl .}
## Set the value of a property of name `field` in a JsObject `obj` to `v`. ## Set the value of a property of name `field` in a JsObject `obj` to `v`.
proc `[]=`*[T](obj: JsObject, field: int, val: T) {. importcpp: setImpl .}
## Set the value of a property of name `field` in a JsObject `obj` to `v`.
proc `[]`*[K: NotString, V](obj: JsAssoc[K, V], field: K): V proc `[]`*[K: NotString, V](obj: JsAssoc[K, V], field: K): V
{. importcpp: getImpl .} {. importcpp: getImpl .}
## Return the value of a property of name `field` from a JsAssoc `obj`. ## Return the value of a property of name `field` from a JsAssoc `obj`.
@ -171,8 +221,9 @@ macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped =
{. importcpp: `importString`, gensym .} {. importcpp: `importString`, gensym .}
helper(`obj`, `value`) helper(`obj`, `value`)
macro `.()`*(obj: JsObject, field: static[cstring], macro `.()`*(obj: JsObject,
args: varargs[JsObject, toJs]): JsObject = field: static[cstring],
args: varargs[JsObject, jsFromAst]): JsObject =
## Experimental "method call" operator for type JsObject. ## Experimental "method call" operator for type JsObject.
## Takes the name of a method of the JavaScript object (`field`) and calls ## Takes the name of a method of the JavaScript object (`field`) and calls
## it with `args` as arguments, returning a JsObject (which may be discarded, ## it with `args` as arguments, returning a JsObject (which may be discarded,
@ -196,9 +247,9 @@ macro `.()`*(obj: JsObject, field: static[cstring],
if not mangledNames.hasKey($field): if not mangledNames.hasKey($field):
mangledNames[$field] = $mangleJsName(field) mangledNames[$field] = $mangleJsName(field)
importString = "#." & mangledNames[$field] & "(@)" importString = "#." & mangledNames[$field] & "(@)"
result = quote do: result = quote:
proc helper(o: JsObject): JsObject proc helper(o: JsObject): JsObject
{. importcpp: `importString`, gensym .} {. importcpp: `importString`, gensym, discardable .}
helper(`obj`) helper(`obj`)
for idx in 0 ..< args.len: for idx in 0 ..< args.len:
let paramName = newIdentNode(!("param" & $idx)) let paramName = newIdentNode(!("param" & $idx))
@ -222,7 +273,8 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
helper(`obj`) helper(`obj`)
macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V], macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
field: static[cstring], value: V): untyped = field: static[cstring],
value: V): untyped =
## Experimental dot accessor (set) for type JsAssoc. ## Experimental dot accessor (set) for type JsAssoc.
## Sets the value of a property of name `field` in a JsObject `x` to `value`. ## Sets the value of a property of name `field` in a JsObject `x` to `value`.
var importString: string var importString: string
@ -238,7 +290,8 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
helper(`obj`, `value`) helper(`obj`, `value`)
macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V], macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
field: static[cstring], args: varargs[untyped]): auto = field: static[cstring],
args: varargs[untyped]): auto =
## Experimental "method call" operator for type JsAssoc. ## Experimental "method call" operator for type JsAssoc.
## Takes the name of a method of the JavaScript object (`field`) and calls ## Takes the name of a method of the JavaScript object (`field`) and calls
## it with `args` as arguments. Here, everything is typechecked, so you do not ## it with `args` as arguments. Here, everything is typechecked, so you do not

View file

@ -49,6 +49,9 @@ type
cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type
pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr`` pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr``
## operator to get a pointer to a variable ## operator to get a pointer to a variable
typedesc* {.magic: TypeDesc.} ## meta type to denote a type description
const const
on* = true ## alias for ``true`` on* = true ## alias for ``true``
off* = false ## alias for ``false`` off* = false ## alias for ``false``
@ -56,6 +59,15 @@ const
{.push warning[GcMem]: off, warning[Uninit]: off.} {.push warning[GcMem]: off, warning[Uninit]: off.}
{.push hints: off.} {.push hints: off.}
proc `or` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
## Constructs an `or` meta class
proc `and` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
## Constructs an `and` meta class
proc `not` *(a: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
## Constructs an `not` meta class
type type
Ordinal* {.magic: Ordinal.}[T] ## Generic ordinal type. Includes integer, Ordinal* {.magic: Ordinal.}[T] ## Generic ordinal type. Includes integer,
## bool, character, and enumeration types ## bool, character, and enumeration types
@ -66,11 +78,11 @@ type
`ref`* {.magic: Pointer.}[T] ## built-in generic traced pointer type `ref`* {.magic: Pointer.}[T] ## built-in generic traced pointer type
`nil` {.magic: "Nil".} `nil` {.magic: "Nil".}
expr* {.magic: Expr, deprecated.} ## meta type to denote an expression (for templates) expr* {.magic: Expr, deprecated.} ## meta type to denote an expression (for templates)
## **Deprecated** since version 0.15. Use ``untyped`` instead. ## **Deprecated** since version 0.15. Use ``untyped`` instead.
stmt* {.magic: Stmt, deprecated.} ## meta type to denote a statement (for templates) stmt* {.magic: Stmt, deprecated.} ## meta type to denote a statement (for templates)
## **Deprecated** since version 0.15. Use ``typed`` instead. ## **Deprecated** since version 0.15. Use ``typed`` instead.
typedesc* {.magic: TypeDesc.} ## meta type to denote a type description
void* {.magic: "VoidType".} ## meta type to denote the absence of any type void* {.magic: "VoidType".} ## meta type to denote the absence of any type
auto* {.magic: Expr.} ## meta type for automatic type determination auto* {.magic: Expr.} ## meta type for automatic type determination
any* = distinct auto ## meta type for any supported type any* = distinct auto ## meta type for any supported type
@ -1332,6 +1344,7 @@ const
hasThreadSupport = compileOption("threads") and not defined(nimscript) hasThreadSupport = compileOption("threads") and not defined(nimscript)
hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
taintMode = compileOption("taintmode") taintMode = compileOption("taintmode")
nimEnableCovariance* = defined(nimEnableCovariance) # or true
when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"): when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"):
# tcc doesn't support TLS # tcc doesn't support TLS

View file

@ -7,7 +7,7 @@ discard """
import macros import macros
macro mixer(n: typed): untyped = macro mixer(n: typed): untyped =
expectKind(n, nnkCharLit) expectKind(n[0], nnkCharLit)
mixer: mixer:
echo "owh" echo "owh"

View file

@ -0,0 +1,45 @@
discard """
output: '''
click at 10,20
lost focus 1
lost focus 2
registered handler for UserEvent 1
registered handler for UserEvent 2
registered handler for UserEvent 3'''
"""
import future
type
Button = object
Event = object
x, y: int
proc onClick(x: Button, handler: proc(x: Event)) =
handler(Event(x: 10, y: 20))
proc onFocusLost(x: Button, handler: proc()) =
handler()
proc onUserEvent(x: Button, eventName: string, handler: proc) =
echo "registered handler for ", eventName
var b = Button()
b.onClick do (e: Event):
echo "click at ", e.x, ",", e.y
b.onFocusLost:
echo "lost focus 1"
b.onFocusLost do:
echo "lost focus 2"
b.onUserEvent "UserEvent 1" do:
discard
b.onUserEvent "UserEvent 2":
discard
b.onUserEvent("UserEvent 3", () => echo "event 3")

25
tests/concepts/t5642.nim Normal file
View file

@ -0,0 +1,25 @@
discard """
output: 9
"""
type DataTable = concept x
x is object
for f in fields(x):
f is seq
type Students = object
id : seq[int]
name : seq[string]
age: seq[int]
proc nrow*(dt: DataTable) : Natural =
var totalLen = 0
for f in fields(dt):
totalLen += f.len
return totalLen
let
stud = Students (id : @[1,2,3], name : @["Vas", "Pas", "NafNaf"], age : @[10,16,32])
echo nrow(stud)

View file

@ -0,0 +1,53 @@
discard """
output: '''
10
20
int
20
3
'''
"""
import typetraits
type
FonConcept = concept x
x.x is int
GenericConcept[T] = concept x
x.x is T
const L = T.name.len
Implementation = object
x: int
Closure = object
f: proc()
proc f1(x: FonConcept): Closure =
result.f = proc () =
echo x.x
proc f2(x: GenericConcept): Closure =
result.f = proc () =
echo x.x
echo GenericConcept.T.name
proc f3[T](x: GenericConcept[T]): Closure =
result.f = proc () =
echo x.x
echo x.L
let x = Implementation(x: 10)
let y = Implementation(x: 20)
let a = x.f1
let b = x.f2
let c = x.f1
let d = y.f2
let e = y.f3
a.f()
d.f()
e.f()

View file

@ -42,7 +42,7 @@ echo p2 is AbstractPointOfFloat # true
echo p2.x is float and p2.y is float # true echo p2.x is float and p2.y is float # true
# https://github.com/nim-lang/Nim/issues/2018 # https://github.com/nim-lang/Nim/issues/2018
type ProtocolFollower = generic type ProtocolFollower = concept
true # not a particularly involved protocol true # not a particularly involved protocol
type ImplementorA = object type ImplementorA = object

View file

@ -0,0 +1,42 @@
discard """
output: "11.0"
"""
type
# A random number generator
Random = object
random: proc(): float
# A generic typeclass for a random var
RandomVar[A] = concept x
var rng: Random
rng.sample(x) is A
# A few concrete instances
Uniform = object
a, b: float
ClosureVar[A] = object
f: proc(rng: var Random): A
# How to sample from various concrete instances
proc sample(rng: var Random, u: Uniform): float = u.a + (u.b - u.a) * rng.random()
proc sample[A](rng: var Random, c: ClosureVar[A]): A = c.f(rng)
proc uniform(a, b: float): Uniform = Uniform(a: a, b: b)
# How to lift a function on values to a function on random variables
proc map[A, B](x: RandomVar[A], f: proc(a: A): B): ClosureVar[B] =
proc inner(rng: var Random): B =
f(rng.sample(x))
result.f = inner
import future
proc fakeRandom(): Random =
result.random = () => 0.5
let x = uniform(1, 10).map((x: float) => 2 * x)
var rng = fakeRandom()
echo rng.sample(x)

View file

@ -0,0 +1,424 @@
discard """
cmd: "nim cpp $file"
output: '''
cat
cat
dog
dog
cat
cat
dog
dog X
cat
cat
dog
dog
dog value
cat value
dog value
cat value
dog
dog
dog value
cat value
dog 1
dog 2
'''
"""
template accept(x) =
static: assert(compiles(x))
template reject(x) =
static: assert(not compiles(x))
import macros
macro skipElse(n: untyped): typed = n[0]
template acceptWithCovariance(x, otherwise): typed =
when nimEnableCovariance:
x
else:
reject(x)
skipElse(otherwise)
type
Animal = object of RootObj
x: string
Dog = object of Animal
y: int
Cat = object of Animal
z: int
AnimalRef = ref Animal
AnimalPtr = ptr Animal
RefAlias[T] = ref T
var dog = new(Dog)
dog.x = "dog"
var cat = new(Cat)
cat.x = "cat"
proc makeDerivedRef(x: string): ref Dog =
new(result)
result.x = x
proc makeDerived(x: string): Dog =
result.x = x
var covariantSeq = @[makeDerivedRef("dog 1"), makeDerivedRef("dog 2")]
var nonCovariantSeq = @[makeDerived("dog 1"), makeDerived("dog 2")]
var covariantArr = [makeDerivedRef("dog 1"), makeDerivedRef("dog 2")]
var nonCovariantArr = [makeDerived("dog 1"), makeDerived("dog 2")]
proc wantsCovariantSeq1(s: seq[ref Animal]) =
for a in s: echo a.x
proc wantsCovariantSeq2(s: seq[AnimalRef]) =
for a in s: echo a.x
proc wantsCovariantSeq3(s: seq[RefAlias[Animal]]) =
for a in s: echo a.x
proc wantsCovariantOperArray(s: openarray[ref Animal]) =
for a in s: echo a.x
proc modifiesCovariantOperArray(s: var openarray[ref Animal]) =
for a in s: echo a.x
proc modifiesDerivedOperArray(s: var openarray[ref Dog]) =
for a in s: echo a.x
proc wantsNonCovariantOperArray(s: openarray[Animal]) =
for a in s: echo a.x
proc wantsCovariantArray(s: array[2, ref Animal]) =
for a in s: echo a.x
proc wantsNonCovariantSeq(s: seq[Animal]) =
for a in s: echo a.x
proc wantsNonCovariantArray(s: array[2, Animal]) =
for a in s: echo a.x
proc modifiesCovariantSeq(s: var seq[ref Animal]) =
for a in s: echo a.x
proc modifiesCovariantArray(s: var array[2, ref Animal]) =
for a in s: echo a.x
proc modifiesCovariantSeq(s: ptr seq[ref Animal]) =
for a in s[]: echo a.x
proc modifiesCovariantArray(s: ptr array[2, ref Animal]) =
for a in s[]: echo a.x
proc modifiesDerivedSeq(s: var seq[ref Dog]) =
for a in s: echo a.x
proc modifiesDerivedArray(s: var array[2, ref Dog]) =
for a in s: echo a.x
proc modifiesDerivedSeq(s: ptr seq[ref Dog]) =
for a in s[]: echo a.x
proc modifiesDerivedArray(s: ptr array[2, ref Dog]) =
for a in s[]: echo a.x
accept:
wantsCovariantArray([AnimalRef(dog), AnimalRef(dog)])
wantsCovariantArray([AnimalRef(cat), AnimalRef(dog)])
wantsCovariantArray([AnimalRef(cat), dog])
# there is a special rule that detects the base
# type of polymorphic arrays
wantsCovariantArray([cat, dog])
acceptWithCovariance:
wantsCovariantArray([cat, cat])
else:
echo "cat"
echo "cat"
var animalRefArray: array[2, ref Animal]
accept:
animalRefArray = [AnimalRef(dog), AnimalRef(dog)]
animalRefArray = [AnimalRef(cat), dog]
acceptWithCovariance:
animalRefArray = [dog, dog]
wantsCovariantArray animalRefArray
else:
echo "dog"
echo "dog"
accept:
var animal: AnimalRef = dog
animal = cat
var vdog: Dog
vdog.x = "dog value"
var vcat: Cat
vcat.x = "cat value"
reject:
vcat = vdog
# XXX: The next two cases seem incosistent, perhaps we should change the rules
accept:
# truncating copies are allowed
var vanimal: Animal = vdog
vanimal = vdog
reject:
# truncating copies are not allowed with arrays
var vanimalArray: array[2, Animal]
var vdogArray = [vdog, vdog]
vanimalArray = vdogArray
accept:
# a more explicit version of a truncating copy that
# should probably always remain allowed
var vnextnimal: Animal = Animal(vdog)
proc wantsRefSeq(x: seq[AnimalRef]) = discard
accept:
wantsCovariantSeq1(@[AnimalRef(dog), AnimalRef(dog)])
wantsCovariantSeq1(@[AnimalRef(cat), AnimalRef(dog)])
wantsCovariantSeq1(@[AnimalRef(cat), dog])
wantsCovariantSeq1(@[cat, dog])
wantsCovariantSeq2(@[AnimalRef(dog), AnimalRef(dog)])
wantsCovariantSeq2(@[AnimalRef(cat), AnimalRef(dog)])
wantsCovariantSeq2(@[AnimalRef(cat), dog])
wantsCovariantSeq2(@[cat, dog])
wantsCovariantSeq3(@[AnimalRef(dog), AnimalRef(dog)])
wantsCovariantSeq3(@[AnimalRef(cat), AnimalRef(dog)])
wantsCovariantSeq3(@[AnimalRef(cat), dog])
wantsCovariantSeq3(@[cat, dog])
wantsCovariantOperArray([cat, dog])
acceptWithCovariance:
wantsCovariantSeq1(@[cat, cat])
wantsCovariantSeq2(@[dog, makeDerivedRef("dog X")])
# XXX: wantsCovariantSeq3(@[cat, cat])
wantsCovariantOperArray(@[cat, cat])
wantsCovariantOperArray([dog, dog])
else:
echo "cat"
echo "cat"
echo "dog"
echo "dog X"
echo "cat"
echo "cat"
echo "dog"
echo "dog"
var dogRefs = @[dog, dog]
var dogRefsArray = [dog, dog]
var animalRefs = @[dog, cat]
accept:
modifiesDerivedArray(dogRefsArray)
modifiesDerivedSeq(dogRefs)
reject modifiesCovariantSeq(dogRefs)
reject modifiesCovariantSeq(addr(dogRefs))
reject modifiesCovariantSeq(dogRefs.addr)
reject modifiesCovariantArray([dog, dog])
reject modifiesCovariantArray(dogRefsArray)
reject modifiesCovariantArray(addr(dogRefsArray))
reject modifiesCovariantArray(dogRefsArray.addr)
var dogValues = @[vdog, vdog]
var dogValuesArray = [vdog, vdog]
var animalValues = @[Animal(vdog), Animal(vcat)]
var animalValuesArray = [Animal(vdog), Animal(vcat)]
wantsNonCovariantSeq animalValues
wantsNonCovariantArray animalValuesArray
reject wantsNonCovariantSeq(dogRefs)
reject modifiesCovariantOperArray(dogRefs)
reject wantsNonCovariantArray(dogRefsArray)
reject wantsNonCovariantSeq(dogValues)
reject wantsNonCovariantArray(dogValuesArray)
reject modifiesValueArray()
modifiesDerivedOperArray dogRefs
reject modifiesDerivedOperArray(dogValues)
reject modifiesDerivedOperArray(animalRefs)
wantsNonCovariantOperArray animalValues
reject wantsNonCovariantOperArray(animalRefs)
reject wantsNonCovariantOperArray(dogRefs)
reject wantsNonCovariantOperArray(dogValues)
var animalRefSeq: seq[ref Animal]
accept:
animalRefSeq = @[AnimalRef(dog), AnimalRef(dog)]
animalRefSeq = @[AnimalRef(cat), dog]
acceptWithCovariance:
animalRefSeq = @[makeDerivedRef("dog 1"), makeDerivedRef("dog 2")]
wantsCovariantSeq1(animalRefSeq)
else:
echo "dog 1"
echo "dog 2"
var pdog: ptr Dog
var pcat: ptr Cat
proc wantsPointer(x: ptr Animal) =
discard
accept:
wantsPointer pdog
wantsPointer pcat
# covariance should be disabled when var is involved
proc wantsVarPointer1(x: var ptr Animal) =
discard
proc wantsVarPointer2(x: var AnimalPtr) =
discard
reject wantsVarPointer1(pdog)
reject wantsVarPointer2(pcat)
# covariance may be allowed for certain extern types
{.emit: """
template <class T> struct FN { typedef void (*type)(T); };
template <class T> struct ARR { typedef T DataType[2]; DataType data; };
""".}
type
MyPtr {.importcpp: "'0 *"} [out T] = object
MySeq {.importcpp: "ARR<'0>", nodecl} [out T] = object
data: array[2, T]
MyAction {.importcpp: "FN<'0>::type"} [in T] = object
var
cAnimal: MyPtr[Animal]
cDog: MyPtr[Dog]
cCat: MyPtr[Cat]
cAnimalFn: MyAction[Animal]
cCatFn: MyAction[Cat]
cDogFn: MyAction[Dog]
cRefAnimalFn: MyAction[ref Animal]
cRefCatFn: MyAction[ref Cat]
cRefDogFn: MyAction[ref Dog]
accept:
cAnimal = cDog
cAnimal = cCat
cDogFn = cAnimalFn
cCatFn = cAnimalFn
cRefDogFn = cRefAnimalFn
cRefCatFn = cRefAnimalFn
reject: cDogFn = cRefAnimalFn
reject: cCatFn = cRefAnimalFn
reject: cCat = cDog
reject: cAnimalFn = cDogFn
reject: cAnimalFn = cCatFn
reject: cRefAnimalFn = cRefDogFn
reject: cRefAnimalFn = cRefCatFn
reject: cRefAnimalFn = cDogFn
var
ptrPtrDog: ptr ptr Dog
ptrPtrAnimal: ptr ptr Animal
reject: ptrPtrDog = ptrPtrAnimal
# Try to break the rules by introducing some tricky
# double indirection types:
var
cPtrRefAnimal: MyPtr[ref Animal]
cPtrRefDog: MyPtr[ref Dog]
cPtrAliasRefAnimal: MyPtr[RefAlias[Animal]]
cPtrAliasRefDog: MyPtr[RefAlias[Dog]]
cDoublePtrAnimal: MyPtr[MyPtr[Animal]]
cDoublePtrDog: MyPtr[MyPtr[Dog]]
reject: cPtrRefAnimal = cPtrRefDog
reject: cDoublePtrAnimal = cDoublePtrDog
reject: cRefAliasPtrAnimal = cRefAliasPtrDog
reject: cPtrRefAnimal = cRefAliasPtrDog
reject: cPtrAliasRefAnimal = cPtrRefDog
var
# Array and Sequence types are covariant only
# when instantiated with ref or ptr types:
cAnimals: MySeq[ref Animal]
cDogs: MySeq[ref Dog]
# "User-defined" pointer types should be OK too:
cAnimalPtrSeq: MySeq[MyPtr[Animal]]
cDogPtrSeq: MySeq[MyPtr[Dog]]
# Value types shouldn't work:
cAnimalValues: MySeq[Animal]
cDogValues: MySeq[Dog]
# Double pointer types should not work either:
cAnimalRefPtrSeq: MySeq[ref MyPtr[Animal]]
cDogRefPtrSeq: MySeq[ref MyPtr[Dog]]
cAnimalPtrPtrSeq: MySeq[ptr ptr Animal]
cDogPtrPtrSeq: MySeq[ptr ptr Dog]
accept:
cAnimals = cDogs
cAnimalPtrSeq = cDogPtrSeq
reject: cAnimalValues = cDogValues
reject: cAnimalRefPtrSeq = cDogRefPtrSeq
reject: cAnimalPtrPtrSeq = cDogPtrPtrSeq
proc wantsAnimalSeq(x: MySeq[Animal]) = discard
proc wantsAnimalRefSeq(x: MySeq[ref Animal]) = discard
proc modifiesAnimalRefSeq(x: var MySeq[ref Animal]) = discard
proc usesAddressOfAnimalRefSeq(x: ptr MySeq[ref Animal]) = discard
accept wantsAnimalSeq(cAnimalValues)
reject wantsAnimalSeq(cDogValues)
reject wantsAnimalSeq(cAnimals)
reject wantsAnimalRefSeq(cAnimalValues)
reject wantsAnimalRefSeq(cDogValues)
accept wantsAnimalRefSeq(cAnimals)
accept wantsAnimalRefSeq(cDogs)
reject modifiesAnimalRefSeq(cAnimalValues)
reject modifiesAnimalRefSeq(cDogValues)
accept modifiesAnimalRefSeq(cAnimals)
reject modifiesAnimalRefSeq(cDogs)
reject usesAddressOfAnimalRefSeq(addr cAnimalValues)
reject usesAddressOfAnimalRefSeq(addr cDogValues)
accept usesAddressOfAnimalRefSeq(addr cAnimals)
reject usesAddressOfAnimalRefSeq(addr cDogs)

View file

@ -0,0 +1,26 @@
discard """
cmd: "nim check $file"
errormsg: "The `in` modifier can be used only with imported types"
nimout: '''
tinvalidinout.nim(14, 7) Error: The `out` modifier can be used only with imported types
tinvalidinout.nim(17, 9) Error: The `in` modifier can be used only with imported types
tinvalidinout.nim(18, 9) Error: The `in` modifier can be used only with imported types
'''
"""
type
Foo {.header: "foo.h", importcpp.} [in T] = object
Bar[out X] = object
x: int
proc f1[in T](x: T) = discard
proc f2[in T](x: T) {.importc: "f", header: "foo.h"}
var
f: Foo[int]
b: Bar[string]
f1 f
f2 b

27
tests/generics/t5570.nim Normal file
View file

@ -0,0 +1,27 @@
discard """
nimout: "type uint32\ntype uint32"
output: "(weight: 17.0, color: 100)"
"""
import macros
type
BaseFruit[T] = object of RootObj
color: T
Banana[T] = object of BaseFruit[uint32]
weight: T
macro printTypeName(typ: typed): untyped =
echo "type ", getType(typ).repr
proc setColor[K](self: var BaseFruit[K], c: int) =
printTypeName(self.color)
self.color = uint32(c)
var x: Banana[float64]
x.weight = 17
printTypeName(x.color)
x.setColor(100)
echo x

View file

@ -0,0 +1,18 @@
discard """
output: "seq[float]\n0"
"""
# https://github.com/nim-lang/Nim/issues/5602
import typetraits
type
Foo[T] = object of RootObj
Bar[T] = object of Foo[seq[T]]
proc p[T](f: Foo[T]): T =
echo T.name
var s: Bar[float]
echo p(s).len # the bug was: p(s) should return seq[float], but returns float instead

30
tests/generics/t5643.nim Normal file
View file

@ -0,0 +1,30 @@
type
Matrix*[M, N: static[int], T: SomeReal] = object
data: ref array[N * M, T]
Matrix64*[M, N: static[int]] = Matrix[M, N, float64]
proc zeros64(M,N: static[int]): Matrix64[M,N] =
new result.data
for i in 0 .. < (M * N):
result.data[i] = 0'f64
proc bar*[M,N: static[int], T](a: Matrix[M,N,T], b: Matrix[M,N,T]) =
discard
let a = zeros64(2,2)
bar(a,a)
# https://github.com/nim-lang/Nim/issues/5643
#
# The test case was failing here, because the compiler failed to
# detect the two matrix instantiations as the same type.
#
# The root cause was that the `T` type variable is a different
# type after the first Matrix type has been matched.
#
# Sigmatch was failing to match the second version of `T`, but
# due to some complex interplay between tyOr, tyTypeDesc and
# tyGenericParam this was allowed to went through. The generic
# instantiation of the second matrix was incomplete and the
# generic cache lookup failed, producing two separate types.

31
tests/generics/t5683.nim Normal file
View file

@ -0,0 +1,31 @@
discard """
output: "perm: 22 det: 22"
"""
type Matrix[M,N: static[int]] = array[M, array[N, float]]
proc det[M,N](a: Matrix[M,N]): int = N*10 + M
proc perm[M,N](a: Matrix[M,N]): int = M*10 + N
const
a = [ [1.0, 2.0]
, [3.0, 4.0]
]
echo "perm: ", a.perm, " det: ", a.det
# This tests multiple instantiations of a generic
# proc involving static params:
type
Vector64*[N: static[int]] = ref array[N, float64]
Array64[N: static[int]] = array[N, float64]
proc vector*[N: static[int]](xs: Array64[N]): Vector64[N] =
new result
for i in 0 .. < N:
result[i] = xs[i]
let v1 = vector([1.0, 2.0, 3.0, 4.0, 5.0])
let v2 = vector([1.0, 2.0, 3.0, 4.0, 5.0])
let v3 = vector([1.0, 2.0, 3.0, 4.0])

View file

@ -0,0 +1,68 @@
template accept(x) =
static: assert(compiles(x))
template reject(x) =
static: assert(not compiles(x))
type
ObjectWithNumber = concept obj
obj.number is int
Foo[T] = object
x: T
type A = object
anumber: int
type B = object
bnumber: int
proc number(a: A): int = a.anumber
proc number(b: B): int = b.bnumber
proc notDistincConcept1(a: ObjectWithNumber, b: ObjectWithNumber) = discard
proc notDistincConcept2(a, b: ObjectWithNumber) = discard
proc distinctConcept1(a, b: distinct ObjectWithNumber) = discard
proc distinctConcept2(a: ObjectWithNumber, b: distinct ObjectWithNumber) = discard
proc distinctConcept3(a: distinct ObjectWithNumber, b: ObjectWithNumber) = discard
proc distinctConcept4(a: distinct ObjectWithNumber, b: distinct ObjectWithNumber) = discard
var a = A(anumber: 5)
var b = B(bnumber: 6)
accept notDistincConcept1(a, a)
accept notDistincConcept1(b, b)
reject notDistincConcept2(a, b)
accept notDistincConcept2(a, a)
accept notDistincConcept2(b, b)
reject notDistincConcept2(a, b)
accept distinctConcept1(a, b)
accept distinctConcept2(a, b)
accept distinctConcept3(a, b)
accept distinctConcept4(a, b)
proc nonDistincGeneric1(a: Foo, b: Foo) = discard
proc nonDistincGeneric2(a, b: Foo) = discard
proc distinctGeneric1(a, b: distinct Foo) = discard
proc distinctGeneric2(a: distinct Foo, b: Foo) = discard
proc distinctGeneric3(a: Foo, b: distinct Foo) = discard
proc distinctGeneric4(a: distinct Foo, b: distinct Foo) = discard
var f1 = Foo[int](x: 10)
var f2 = Foo[string](x: "x")
accept nonDistincGeneric1(f1, f1)
accept nonDistincGeneric1(f2, f2)
reject nonDistincGeneric1(f1, f2)
accept nonDistincGeneric2(f1, f1)
accept nonDistincGeneric2(f2, f2)
reject nonDistincGeneric2(f1, f2)
accept distinctGeneric1(f1, f1)
accept distinctGeneric2(f1, f1)
accept distinctGeneric3(f1, f1)
accept distinctGeneric4(f1, f1)

View file

@ -0,0 +1,78 @@
template accept(x) =
static: assert(compiles(x))
template reject(x) =
static: assert(not compiles(x))
type
BaseObj = object of RootObj
DerivedObj = object of BaseObj
NonDerivedObj = object
Container[T] = object
var base: BaseObj
var derived: DerivedObj
var nonDerived: NonDerivedObj
var baseContainer: Container[BaseObj]
var derivedContainer: Container[DerivedObj]
var nonDerivedContainer: Container[NonDerivedObj]
# We can fake covariance by listing some specific derived types that
# will be allowed with our overload. This is not a real covariance,
# because there will be multiple instantiations of the proc, but for
# many purposes, it will suffice:
proc wantsSpecificContainers(c: Container[BaseObj or DerivedObj]) = discard
accept wantsSpecificContainers(baseContainer)
accept wantsSpecificContainers(derivedContainer)
reject wantsSpecificContainers(nonDerivedContainer)
reject wantsSpecificContainers(derived)
# Now, let's make a more general solution able to catch all derived types:
type
DerivedFrom[T] = concept type D
var derived: ref D
var base: ref T = derived
proc wantsDerived(x: DerivedFrom[BaseObj]) = discard
accept wantsDerived(base)
accept wantsDerived(derived)
reject wantsDerived(nonDerived)
reject wantsDerived(baseContainer)
proc wantsDerivedContainer(c: Container[DerivedFrom[BaseObj]]) = discard
accept wantsDerivedContainer(baseContainer)
accept wantsDerivedContainer(derivedContainer)
reject wantsDerivedContainer(nonDerivedContainer)
# The previous solutions were solving the problem for a single overload.
# Let's solve it for multiple overloads by introducing a converter:
type
OtherContainer[T] = object
proc wantsBaseContainer1(c: OtherContainer[BaseObj]) = discard
proc wantsBaseContainer2(c: OtherContainer[BaseObj]) = discard
converter derivedToBase(c: OtherContainer[DerivedFrom[BaseObj]]): OtherContainer[BaseObj] = discard
block:
var baseContainer: OtherContainer[BaseObj]
var derivedContainer: OtherContainer[DerivedObj]
var nonDerivedContainer: OtherContainer[NonDerivedObj]
accept wantsBaseContainer1(derivedContainer)
reject wantsBaseContainer1(nonDerivedContainer)
accept wantsBaseContainer2(derivedContainer)
reject wantsBaseContainer2(nonDerivedContainer)

View file

@ -0,0 +1,39 @@
discard """
output: '''
@[1, 2]
@[3, 4]
1
'''
"""
# https://github.com/nim-lang/Nim/issues/5756
type
Vec*[N : static[int]] = object
x: int
arr*: array[N, int32]
Mat*[M,N: static[int]] = object
x: int
arr*: array[M, Vec[N]]
proc vec2*(x,y:int32) : Vec[2] =
result.arr = [x,y]
result.x = 10
proc mat2*(a,b: Vec[2]): Mat[2,2] =
result.arr = [a,b]
result.x = 20
const M = mat2(vec2(1, 2), vec2(3, 4))
let m1 = M
echo @(m1.arr[0].arr)
echo @(m1.arr[1].arr)
proc foo =
let m2 = M
echo m1.arr[0].arr[0]
foo()

View file

@ -0,0 +1,28 @@
discard """
output: '''type(c) = GenAlias[system.int]
T = int
seq[int]
'''
"""
import typetraits
type
Gen[T] = object
x: T
GenAlias[T] = Gen[seq[T]]
proc f1[T](x: Gen[T]) =
echo T.name
proc f2[T](x: GenAlias[T]) =
echo "type(c) = ", type(x).name
echo "T = ", T.name
f1 x
let
y = Gen[seq[int]](x: @[10])
f2 y

View file

@ -0,0 +1,28 @@
discard """
errormsg: "got (ref Matrix[2, 2, system.float], ref Matrix[2, 1, system.float])"
line: 27
"""
type
Matrix[M,N: static[int]; T: SomeReal] = distinct array[0..(M*N - 1), T]
let a = new Matrix[2,2,float]
let b = new Matrix[2,1,float]
proc foo[M,N: static[int],T](a: ref Matrix[M, N, T], b: ref Matrix[M, N, T])=
discard
foo(a, a)
proc bar[M,N: static[int],T](a: ref Matrix[M, M, T], b: ref Matrix[M, N, T])=
discard
bar(a, b)
bar(a, a)
proc baz[M,N: static[int],T](a: ref Matrix[N, N, T], b: ref Matrix[M, N, T])=
discard
baz(a, a)
baz(a, b)

View file

@ -0,0 +1,20 @@
type NSPasteboardItem* = ptr object
type NSPasteboard* = ptr object
type NSArrayAbstract = ptr object {.inheritable.}
type NSMutableArrayAbstract = ptr object of NSArrayAbstract
type NSArray*[T] = ptr object of NSArrayAbstract
type NSMutableArray*[T] = ptr object of NSArray[T]
proc newMutableArrayAbstract*(): NSMutableArrayAbstract = discard
template newMutableArray*(T: typedesc): NSMutableArray[T] =
cast[NSMutableArray[T]](newMutableArrayAbstract())
proc writeObjects*(p: NSPasteboard, o: NSArray[NSPasteboardItem]) = discard
let a = newMutableArray NSPasteboardItem
var x: NSMutableArray[NSPasteboardItem]
var y: NSArray[NSPasteboardItem] = x
writeObjects(nil, a)

View file

@ -1,5 +1,5 @@
discard """ discard """
output: '''true output: '''
true true
true true
true true
@ -14,10 +14,18 @@ true
true true
true true
true true
true''' true
true
3
2
12
Event { name: 'click: test' }
Event { name: 'reloaded: test' }
Event { name: 'updates: test' }
'''
""" """
import macros, jsffi import macros, jsffi, jsconsole
# Tests for JsObject # Tests for JsObject
# Test JsObject []= and [] # Test JsObject []= and []
@ -55,8 +63,8 @@ block:
block: block:
proc test(): bool = proc test(): bool =
let obj = newJsObject() let obj = newJsObject()
obj.`?!$` = proc(x, y, z: int, t: string): string = t & $(x + y + z) obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z)
obj.`?!$`(1, 2, 3, "Result is: ").to(string) == "Result is: 6" obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == "Result is: 6"
echo test() echo test()
# Test JsObject []() # Test JsObject []()
@ -265,3 +273,47 @@ block:
let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever)) let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever))
obj.onWhatever(1) == 10 obj.onWhatever(1) == 10
echo test() echo test()
block:
{.emit: "function jsProc(n) { return n; }" .}
proc jsProc(x: int32): JsObject {.importc: "jsProc".}
proc test() =
var x = jsProc(1)
var y = jsProc(2)
console.log x + y
console.log ++x
x += jsProc(10)
console.log x
test()
import macros
block:
{.emit:
"""
function Event(name) { this.name = name; }
function on(eventName, eventHandler) { eventHandler(new Event(eventName + ": test")); }
var jslib = { "on": on, "subscribe": on };
"""
.}
type Event = object
name: cstring
proc on(event: cstring, handler: proc) {.importc: "on".}
var jslib {.importc: "jslib", nodecl.}: JsObject
on("click") do (e: Event):
console.log e
jslib.on "reloaded" do:
console.log jsarguments[0]
# this test case is different from the above, because
# `subscribe` is not overloaded in the current scope
jslib.subscribe "updates":
console.log jsarguments[0]

View file

@ -1,8 +1,7 @@
import macros import macros
macro match*(s: cstring|string; pos: int; sections: untyped): untyped = macro match*(s: cstring|string; pos: int; sections: varargs[untyped]): untyped =
for sec in sections.children: for sec in sections:
expectKind sec, nnkOfBranch expectKind sec, nnkOfBranch
expectLen sec, 2 expectLen sec, 2
result = newStmtList() result = newStmtList()

View file

@ -0,0 +1,513 @@
discard """
nimout: '''
StmtList
Ident !"foo"
Call
Ident !"foo"
Call
Ident !"foo"
Ident !"x"
Command
Ident !"foo"
Ident !"x"
Call
Ident !"foo"
StmtList
DiscardStmt
Empty
Call
Ident !"foo"
StmtList
DiscardStmt
Empty
Call
Ident !"foo"
StrLit test
StmtList
DiscardStmt
Empty
Call
Ident !"foo"
StrLit test
StmtList
DiscardStmt
Empty
Command
Ident !"foo"
StrLit test
StmtList
DiscardStmt
Empty
Command
Ident !"foo"
StrLit test
StmtList
DiscardStmt
Empty
Command
Ident !"foo"
IntLit 1
Par
Infix
Ident !"+"
IntLit 2
IntLit 3
StmtList
DiscardStmt
Empty
Command
Ident !"foo"
IntLit 1
Par
Infix
Ident !"+"
IntLit 2
IntLit 3
StmtList
DiscardStmt
Empty
Call
Ident !"foo"
Do
Empty
Empty
Empty
FormalParams
Empty
IdentDefs
Ident !"x"
Empty
Empty
Empty
Empty
StmtList
DiscardStmt
Empty
Call
Ident !"foo"
Do
Empty
Empty
Empty
FormalParams
Empty
IdentDefs
Ident !"x"
Ident !"int"
Empty
Empty
Empty
StmtList
DiscardStmt
Empty
Call
Ident !"foo"
Do
Empty
Empty
Empty
FormalParams
Ident !"int"
IdentDefs
Ident !"x"
Ident !"int"
Empty
Empty
Empty
StmtList
DiscardStmt
Empty
Command
Ident !"foo"
Ident !"x"
Do
Empty
Empty
Empty
FormalParams
Empty
IdentDefs
Ident !"y"
Empty
Empty
Empty
Empty
StmtList
DiscardStmt
Empty
Call
Ident !"foo"
StmtList
DiscardStmt
Empty
Else
StmtList
DiscardStmt
Empty
Call
Ident !"foo"
StmtList
DiscardStmt
Empty
StmtList
DiscardStmt
Empty
Else
StmtList
DiscardStmt
Empty
Command
Ident !"foo"
Ident !"x"
Do
Empty
Empty
Empty
FormalParams
Empty
IdentDefs
Ident !"y"
Empty
Empty
Empty
Empty
StmtList
DiscardStmt
Empty
Do
Empty
Empty
Empty
FormalParams
Ident !"int"
IdentDefs
Ident !"z"
Empty
Empty
Empty
Empty
StmtList
DiscardStmt
Empty
Do
Empty
Empty
Empty
FormalParams
Ident !"int"
IdentDefs
Ident !"w"
Ident !"int"
Empty
Empty
Empty
StmtList
DiscardStmt
Empty
StmtList
DiscardStmt
Empty
Else
StmtList
DiscardStmt
Empty
Call
Ident !"foo"
Ident !"x"
Call
Ident !"bar"
StmtList
DiscardStmt
Empty
Else
StmtList
DiscardStmt
Empty
VarSection
IdentDefs
Ident !"a"
Empty
Ident !"foo"
VarSection
IdentDefs
Ident !"a"
Empty
Call
Ident !"foo"
VarSection
IdentDefs
Ident !"a"
Empty
Call
Ident !"foo"
Ident !"x"
VarSection
IdentDefs
Ident !"a"
Empty
Command
Ident !"foo"
Ident !"x"
VarSection
IdentDefs
Ident !"a"
Empty
Call
Ident !"foo"
StmtList
DiscardStmt
Empty
VarSection
IdentDefs
Ident !"a"
Empty
Call
Ident !"foo"
StmtList
DiscardStmt
Empty
VarSection
IdentDefs
Ident !"a"
Empty
Call
Ident !"foo"
StmtList
DiscardStmt
Empty
Else
StmtList
DiscardStmt
Empty
VarSection
IdentDefs
Ident !"a"
Empty
Command
Ident !"foo"
Ident !"x"
Do
Empty
Empty
Empty
FormalParams
Empty
IdentDefs
Ident !"y"
Empty
Empty
Empty
Empty
StmtList
DiscardStmt
Empty
Else
StmtList
DiscardStmt
Empty
Asgn
Ident !"a"
Ident !"foo"
Asgn
Ident !"a"
Call
Ident !"foo"
Asgn
Ident !"a"
Call
Ident !"foo"
Ident !"x"
Asgn
Ident !"a"
Command
Ident !"foo"
Ident !"x"
Asgn
Ident !"a"
Call
Ident !"foo"
StmtList
DiscardStmt
Empty
Asgn
Ident !"a"
Call
Ident !"foo"
StmtList
DiscardStmt
Empty
Asgn
Ident !"a"
Call
Ident !"foo"
StmtList
DiscardStmt
Empty
Else
StmtList
DiscardStmt
Empty
Asgn
Ident !"a"
Command
Ident !"foo"
Ident !"x"
Do
Empty
Empty
Empty
FormalParams
Empty
IdentDefs
Ident !"y"
Empty
Empty
Empty
Empty
StmtList
DiscardStmt
Empty
Else
StmtList
DiscardStmt
Empty
Call
DotExpr
Ident !"result"
Ident !"add"
BracketExpr
Call
Ident !"quote"
StmtList
DiscardStmt
Empty
IntLit 0
'''
"""
import macros
dumpTree:
# simple calls
foo
foo()
foo(x)
foo x
foo:
discard
foo do:
discard
foo("test"):
discard
foo("test") do:
discard
foo "test":
discard
foo "test" do:
discard
# more complicated calls
foo 1, (2+3):
discard
foo 1, (2+3) do:
discard
foo do (x):
discard
foo do (x: int):
discard
foo do (x: int) -> int:
discard
foo x do (y):
discard
# extra blocks
foo:
discard
else:
discard
foo do:
discard
do:
discard
else:
discard
foo x do (y):
discard
do (z) -> int:
discard
do (w: int) -> int:
discard
do:
discard
else:
discard
# call with blocks as a param
foo(x, bar do:
discard
else:
discard
)
# introduce a variable
var a = foo
var a = foo()
var a = foo(x)
var a = foo x
var a = foo:
discard
var a = foo do:
discard
var a = foo do:
discard
else:
discard
var a = foo x do (y):
discard
else:
discard
# assignments
a = foo
a = foo()
a = foo(x)
a = foo x
a = foo:
discard
a = foo do:
discard
a = foo do:
discard
else:
discard
a = foo x do (y):
discard
else:
discard
# some edge cases
result.add((quote do:
discard
)[0])

View file

@ -1,7 +1,10 @@
discard """ discard """
output: '''{"age": 12, "bio": "\u042F Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"} output: '''{"age": 12, "bio": "\u042F Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"}
true true
true''' true
alpha 100
omega 200
'''
""" """
import marshal import marshal
@ -83,3 +86,22 @@ var instance1 = Person(name: "Cletus", age: 12,
echo($$instance1) echo($$instance1)
echo(to[Person]($$instance1).bio == instance1.bio) echo(to[Person]($$instance1).bio == instance1.bio)
echo(to[Person]($$instance1).blob == instance1.blob) echo(to[Person]($$instance1).blob == instance1.blob)
# bug 5757
type
Something = object
x: string
y: int
var data1 = """{"x": "alpha", "y": 100}"""
var data2 = """{"x": "omega", "y": 200}"""
var r = to[Something](data1)
echo r.x, " ", r.y
r = to[Something](data2)
echo r.x, " ", r.y

View file

@ -0,0 +1,13 @@
type
SomeObj = object of RootObj
Foo[T, U] = object
x: T
y: U
template someTemplate[T](): tuple[id: int32, obj: T] =
var result: tuple[id: int32, obj: T] = (0'i32, T())
result
let ret = someTemplate[SomeObj]()

View file

@ -59,14 +59,13 @@ template wrap(body: typed): untyped =
macro makeProc(): typed = macro makeProc(): typed =
# Make a template tree # Make a template tree
result = (quote do: result = quote do:
proc someProc* = proc someProc* =
wrap do: wrap do:
let x = 123 let x = 123
# Implicit conversion here # Implicit conversion here
let s: string = x let s: string = x
echo s echo s
)
makeProc() makeProc()

6
tests/types/t5640.nim Normal file
View file

@ -0,0 +1,6 @@
type
vecBase[I: static[int]] = distinct array[I, float32]
vec2* = vecBase[2]
var v = vec2([0.0'f32, 0.0'f32])

21
tests/types/t5648.nim Normal file
View file

@ -0,0 +1,21 @@
discard """
output: "ptr Foo"
"""
import typetraits
type Foo = object
bar*: int
proc main() =
var f = create(Foo)
f.bar = 3
echo f.type.name
discard realloc(f, 0)
var g = Foo()
g.bar = 3
main()

View file

@ -0,0 +1,22 @@
type
Bar[T] = Foo[T, T]
Baz[T] = proc (x: Foo[T, T])
GenericAlias[T] = Foo[T]
GenericAlias2[T] = Foo[Baz[T]]
Concrete1 = Foo[int, float]
Concrete2 = proc(x: proc(a: Foo[int, float]))
Foo[T, U] = object
x: T
y: U
var
x1: Bar[float]
x2: Baz[int]
x3: Concrete1
x4: Concrete2
x5: GenericAlias[int]
x6: GenericAlias2[string]

View file

@ -33,13 +33,13 @@ proc getSupporters(self: BountySource): Future[JsonNode] {.async.} =
let response = await self.client.get(apiUrl & let response = await self.client.get(apiUrl &
"/supporters?order=monthly&per_page=200&team_slug=" & self.team) "/supporters?order=monthly&per_page=200&team_slug=" & self.team)
doAssert response.status.startsWith($Http200) doAssert response.status.startsWith($Http200)
return parseJson(response.body) return parseJson(await response.body)
proc getGithubUser(username: string): Future[JsonNode] {.async.} = proc getGithubUser(username: string): Future[JsonNode] {.async.} =
let client = newAsyncHttpClient() let client = newAsyncHttpClient()
let response = await client.get(githubApiUrl & "/users/" & username) let response = await client.get(githubApiUrl & "/users/" & username)
if response.status.startsWith($Http200): if response.status.startsWith($Http200):
return parseJson(response.body) return parseJson(await response.body)
else: else:
echo("Could not get Github user: ", username, ". ", response.status) echo("Could not get Github user: ", username, ". ", response.status)
return nil return nil