diff options
54 files changed, 2478 insertions, 339 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 49ca1c5e0..63b656715 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -224,7 +224,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # already 32 flags! + TSymFlag* = enum # already 33 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module 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 nfIsRef # this node is a 'ref' node; used for the VM 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] - 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 + # tyArray type represeting a varargs list tfNoSideEffect, # procedure type does not allow side effects tfFinal, # is the object final? tfInheritable, # is the object inheritable? @@ -506,6 +508,9 @@ type tfTriggersCompileTime # uses the NimNode type which make the proc # implicitly '.compiletime' 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] @@ -1002,15 +1007,17 @@ proc add*(father, son: PNode) = if isNil(father.sons): father.sons = @[] add(father.sons, son) -proc `[]`*(n: PNode, i: int): PNode {.inline.} = - result = n.sons[i] +type Indexable = PNode | PType + +template `[]`*(n: Indexable, i: int): Indexable = + n.sons[i] template `-|`*(b, s: untyped): untyped = (if b >= 0: b else: s.len + b) # son access operators with support for negative indices -template `{}`*(n: PNode, i: int): untyped = n[i -| n] -template `{}=`*(n: PNode, i: int, s: PNode) = +template `{}`*(n: Indexable, i: int): untyped = n[i -| n] +template `{}=`*(n: Indexable, i: int, s: Indexable) = n.sons[i -| n] = s when defined(useNodeIds): @@ -1346,6 +1353,9 @@ proc initIdTable*(x: var TIdTable) = x.counter = 0 newSeq(x.data, StartSize) +proc newIdTable*: TIdTable = + initIdTable(result) + proc resetIdTable*(x: var TIdTable) = x.counter = 0 # clear and set to old initial size: diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 77108eb7b..1fc8b7cea 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -70,6 +70,8 @@ proc debug*(n: PNode) {.deprecated.} template mdbg*: bool {.dirty.} = when compiles(c.module): c.module.fileIdx == gProjectMainIdx + elif compiles(c.c.module): + c.c.module.fileIdx == gProjectMainIdx elif compiles(m.c.module): m.c.module.fileIdx == gProjectMainIdx elif compiles(cl.c.module): @@ -79,6 +81,8 @@ template mdbg*: bool {.dirty.} = p.lex.fileIdx == gProjectMainIdx else: p.module.module.fileIdx == gProjectMainIdx + elif compiles(m.module.fileIdx): + m.module.fileIdx == gProjectMainIdx elif compiles(L.fileIdx): L.fileIdx == gProjectMainIdx else: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index d244153db..f5b99c405 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -269,7 +269,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # little HACK to support the new 'var T' as return type: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) return - let ty = skipTypes(dest.t, abstractRange) + let ty = skipTypes(dest.t, abstractRange + tyUserTypeClasses) case ty.kind of tyRef: genRefAssign(p, dest, src, flags) @@ -758,7 +758,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = genRecordFieldAux(p, e, d, a) var r = rdLoc(a) var f = e.sons[1].sym - let ty = skipTypes(a.t, abstractInst) + let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses) if ty.kind == tyTuple: # we found a unique tuple type which lacks field information # so we use Field$i @@ -2205,7 +2205,7 @@ proc getNullValueAux(p: BProc; obj, cons: PNode, result: var Rope) = let field = obj.sym for i in 1..<cons.len: 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]) return elif i == field.position: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 2b7cfaea1..02eb5ba82 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -164,7 +164,7 @@ proc mapType(typ: PType): TCTypeKind = of tySet: result = mapSetType(typ) of tyOpenArray, tyArray, tyVarargs: result = ctArray of tyObject, tyTuple: result = ctStruct - of tyUserTypeClass, tyUserTypeClassInst: + of tyUserTypeClasses: internalAssert typ.isResolvedUserTypeClass return mapType(typ.lastSon) 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 = let origType = t - var t = skipTypes(origType, irrelevantForBackend) + var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) let sig = hashType(origType) result = m.typeInfoMarker.getOrDefault(sig) @@ -1131,6 +1131,9 @@ proc genTypeInfo(m: BModule, t: PType): Rope = of tyStatic: if t.n != nil: result = genTypeInfo(m, lastSon t) else: internalError("genTypeInfo(" & $t.kind & ')') + of tyUserTypeClasses: + internalAssert t.isResolvedUserTypeClass + return genTypeInfo(m, t.lastSon) of tyProc: if t.callConv != ccClosure: genTypeInfoAuxBase(m, t, t, result, rope"0") diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 4303fd6c8..a4f47ac72 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -103,3 +103,4 @@ proc initDefines*() = defineSymbol("nimNewShiftOps") defineSymbol("nimDistros") defineSymbol("nimHasCppDefine") + defineSymbol("nimGenericInOutFlags") diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 5bd274a3e..fda0b79dd 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -90,7 +90,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode = result = newNodeI(nkArgList, n.info) for i in 1 .. givenRegularParams: - result.addSon n.sons[i] + result.addSon n[i] # handle parameters with default values, which were # not supplied by the user diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 3a97f1ed2..e416a3826 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -113,6 +113,7 @@ type errGenericLambdaNotAllowed, errProcHasNoConcreteType, errCompilerDoesntSupportTarget, + errInOutFlagNotExtern, errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, @@ -380,6 +381,7 @@ const "of the generic paramers can be inferred from the expected signature.", 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", + errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\'", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", diff --git a/compiler/parser.nim b/compiler/parser.nim index fabe4bcc8..d4c44788e 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -67,6 +67,8 @@ proc parseTry(p: var TParser; isExpr: bool): PNode proc parseCase(p: var TParser): PNode proc parseStmtPragma(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 proc getTok(p: var TParser) = @@ -194,7 +196,6 @@ proc newIdentNodeP(ident: PIdent, p: TParser): PNode = proc parseExpr(p: var TParser): PNode proc parseStmt(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 isSigilLike(tok: TToken): bool {.inline.} = @@ -365,7 +366,10 @@ proc colonOrEquals(p: var TParser, a: PNode): PNode = proc exprColonEqExpr(p: var TParser): PNode = #| exprColonEqExpr = expr (':'|'=' expr)? var a = parseExpr(p) - result = colonOrEquals(p, a) + if p.tok.tokType == tkDo: + result = postExprBlocks(p, a) + else: + result = colonOrEquals(p, a) proc exprList(p: var TParser, endTok: TTokType, result: PNode) = #| exprList = expr ^+ comma @@ -520,7 +524,9 @@ proc parsePar(p: var TParser): PNode = result.add(parseStmtPragma(p)) elif p.tok.tokType != tkParRi: 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 let asgn = newNodeP(nkAsgn, p) getTok(p) @@ -669,7 +675,6 @@ proc namedParams(p: var TParser, callee: PNode, # progress guaranteed exprColonEqExprListAux(p, endTok, result) -proc parseMacroColon(p: var TParser, x: PNode): PNode proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? #| | doBlocks @@ -696,14 +701,6 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result.sons[1].kind == nkExprColonExpr: 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: # progress guaranteed result = dotExpr(p, result) @@ -735,10 +732,7 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = if p.tok.tokType != tkComma: break getTok(p) optInd(p, x) - if p.tok.tokType == tkDo: - parseDoBlocks(p, result) - else: - result = parseMacroColon(p, result) + result = postExprBlocks(p, result) break else: break @@ -977,16 +971,9 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode = let params = parseParamList(p, retColon=false) let pragmas = optPragmas(p) colcom(p, result) - result = newProcNode(nkDo, info, parseStmt(p), - 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)) + result = parseStmt(p) + if params.kind != nkEmpty: + result = newProcNode(nkDo, info, result, params = params, pragmas = pragmas) proc parseProcExpr(p: var TParser, isExpr: bool): PNode = #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? @@ -1162,49 +1149,75 @@ proc makeCall(n: PNode): PNode = result = newNodeI(nkCall, n.info) result.add n -proc parseMacroColon(p: var TParser, x: PNode): PNode = - #| macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt - #| | IND{=} 'elif' expr ':' stmt - #| | IND{=} 'except' exprList ':' stmt - #| | IND{=} 'else' ':' stmt )* +proc postExprBlocks(p: var TParser, x: PNode): PNode = + #| postExprBlocks = ':' stmt? ( IND{=} doBlock + #| | IND{=} 'of' exprList ':' stmt + #| | IND{=} 'elif' expr ':' stmt + #| | IND{=} 'except' exprList ':' stmt + #| | IND{=} 'else' ':' stmt )* 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) getTok(p) skipComment(p, result) - let stmtList = newNodeP(nkStmtList, p) if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: - let body = parseStmt(p) - stmtList.add body - #addSon(result, makeStmtList(body)) - # progress guaranteed + var stmtList = newNodeP(nkStmtList, p) + stmtList.add parseStmt(p) + # to keep backwards compatibility (see tests/vm/tstringnil) + 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): - var b: PNode - case p.tok.tokType - of tkOf: - b = newNodeP(nkOfBranch, p) - exprList(p, tkColon, b) - of tkElif: - b = newNodeP(nkElifBranch, p) - getTok(p) - optInd(p, b) - addSon(b, parseExpr(p)) - of tkExcept: - b = newNodeP(nkExceptBranch, p) - exprList(p, tkColon, b) - of tkElse: - b = newNodeP(nkElse, p) + var nextBlock: PNode + let nextToken = p.tok.tokType + if nextToken == tkDo: + let info = parLineInfo(p) getTok(p) - else: break - eat(p, tkColon) - addSon(b, parseStmt(p)) - addSon(stmtList, b) - if b.kind == nkElse: break - if stmtList.len == 1 and stmtList[0].kind == nkStmtList: - # to keep backwards compatibility (see tests/vm/tstringnil) - result.add stmtList[0] - else: - result.add stmtList + nextBlock = parseDoBlock(p, info) + 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: + nextBlock = newNodeP(nkExceptBranch, p) + exprList(p, tkColon, nextBlock) + of tkElse: + nextBlock = newNodeP(nkElse, p) + getTok(p) + else: break + eat(p, tkColon) + nextBlock.addSon parseStmt(p) + + nextBlock.flags.incl nfBlockArg + result.add nextBlock + + if nextBlock.kind == nkElse: break + else: + if openingParams.kind != nkEmpty: + parMessage(p, errTokenExpected, ":") proc parseExprStmt(p: var TParser): PNode = #| exprStmt = simpleExpr @@ -1219,12 +1232,7 @@ proc parseExprStmt(p: var TParser): PNode = getTok(p) optInd(p, result) var b = parseExpr(p) - if p.tok.tokType == tkColon and p.tok.indent < 0: - if b.kind != nkEmpty: - let call = makeCall(b) - call.add parseDoBlock(p, parLineInfo(p)) - parseDoBlocks(p, call) - b = call + b = postExprBlocks(p, b) addSon(result, a) addSon(result, b) else: @@ -1247,11 +1255,7 @@ proc parseExprStmt(p: var TParser): PNode = optInd(p, result) else: result = a - if p.tok.tokType == tkDo and p.tok.indent < 0: - result = makeCall(result) - parseDoBlocks(p, result) - return result - result = parseMacroColon(p, result) + result = postExprBlocks(p, result) proc parseModuleName(p: var TParser, kind: TNodeKind): PNode = result = parseExpr(p) @@ -1341,7 +1345,9 @@ proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode = # NL terminates: addSon(result, ast.emptyNode) else: - addSon(result, parseExpr(p)) + var e = parseExpr(p) + e = postExprBlocks(p, e) + addSon(result, e) proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = #| condStmt = expr colcom stmt COMMENT? @@ -1514,6 +1520,13 @@ proc parseGenericParam(p: var TParser): PNode = # progress guaranteed while true: 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: a = parseSymbol(p) if a.kind == nkEmpty: return @@ -1542,7 +1555,7 @@ proc parseGenericParamList(p: var TParser): PNode = getTok(p) optInd(p, result) # progress guaranteed - while p.tok.tokType in {tkSymbol, tkAccent}: + while p.tok.tokType in {tkSymbol, tkAccent, tkIn, tkOut}: var a = parseGenericParam(p) addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break @@ -1893,14 +1906,7 @@ proc parseVariable(p: var TParser): PNode = #| variable = (varTuple / identColonEquals) colonBody? indAndComment if p.tok.tokType == tkParLe: result = parseVarTuple(p) else: result = parseIdentColonEquals(p, {withPragma}) - if p.tok.tokType == tkColon and p.tok.indent < 0: - 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 + result{-1} = postExprBlocks(p, result{-1}) indAndComment(p, result) proc parseBind(p: var TParser, k: TNodeKind): PNode = diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim index b59fbc6cf..fa4ccc139 100644 --- a/compiler/pbraces.nim +++ b/compiler/pbraces.nim @@ -1336,6 +1336,11 @@ proc parseGenericParam(p: var TParser): PNode = result = newNodeP(nkIdentDefs, p) while true: case p.tok.tokType + of tkIn, tkOut: + let t = p.tok.tokType + getTok(p) + expectIdent(p) + a = parseSymbol(p) of tkSymbol, tkAccent: a = parseSymbol(p) if a.kind == nkEmpty: return diff --git a/compiler/sem.nim b/compiler/sem.nim index ab3c5cec8..9f80e1399 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -351,8 +351,8 @@ when false: for i in 0 ..< n.safeLen: resetSemFlag(n[i]) -proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, - flags: TExprFlags): PNode = +proc semAfterMacroCall(c: PContext, call, macroResult: PNode, + s: PSym, flags: TExprFlags): PNode = ## Semantically check the output of a macro. ## This involves processes such as re-checking the macro output for type ## 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) c.friendModules.add(s.owner.getModule) - result = n - excl(n.flags, nfSem) + result = macroResult + excl(result.flags, nfSem) #resetSemFlag n if s.typ.sons[0] == nil: result = semStmt(c, result) @@ -378,13 +378,26 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, of tyStmt: result = semStmt(c, result) of tyTypeDesc: - if n.kind == nkStmtList: result.kind = nkStmtListType + if result.kind == nkStmtList: result.kind = nkStmtListType var typ = semTypeNode(c, result, nil) result.typ = makeTypeDesc(c, typ) #result = symNodeFromType(c, typ, n.info) 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 = 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])) dec(evalTemplateCounter) discard c.friendModules.pop() @@ -409,7 +422,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, # c.evalContext = c.createEvalContext(emStatic) result = evalMacroCall(c.module, c.cache, n, nOrig, sym) if efNoSemCheck notin flags: - result = semAfterMacroCall(c, result, sym, flags) + result = semAfterMacroCall(c, n, result, sym, flags) result = wrapInComesFrom(nOrig.info, result) popInfoContext() diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 1089ab7db..4fa4f7f32 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -429,7 +429,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = for i in 1..sonsLen(n)-1: let formal = s.ast.sons[genericParamsPos].sons[i-1].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 var newInst = generateInstance(c, s, m.bindings, n.info) newInst.typ.flags.excl tfUnresolved diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 8f2c802de..5da2b70fa 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -277,6 +277,10 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType = assert n != nil 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, sons: seq[PType]): PType = result = newType(kind, getCurrOwner(c)) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 25f62983d..59fa208d2 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -16,7 +16,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, styleCheckUse(n.info, s) pushInfoContext(n.info) 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() proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode @@ -47,10 +47,11 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = #raiseRecoverableError("") result = errorNode(c, n) if result.typ == nil or result.typ == enforceVoidContext: - # we cannot check for 'void' in macros ... - localError(n.info, errExprXHasNoType, - renderTree(result, {renderNoComments})) - result.typ = errorType(c) + if n.kind != nkStmtList: + # we cannot check for 'void' in macros ... + localError(n.info, errExprXHasNoType, + renderTree(result, {renderNoComments})) + result.typ = errorType(c) else: if efNoProcvarCheck notin flags: semProcvarCheck(c, result) if result.typ.kind == tyVar: result = newDeref(result) @@ -682,27 +683,9 @@ proc bracketedMacro(n: PNode): PSym = if result.kind notin {skMacro, skTemplate}: result = nil -proc semBracketedMacro(c: PContext; outer, inner: PNode; s: PSym; - flags: TExprFlags): PNode = - # We received untransformed bracket expression coming from macroOrTmpl[]. - # 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 setGenericParams(c: PContext, n: PNode) = + for i in 1 .. <n.len: + n[i].typ = semTypeNode(c, n[i], nil) proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = result = n @@ -744,7 +727,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = elif n.sons[0].kind == nkBracketExpr: let s = bracketedMacro(n.sons[0]) 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 semOpAux(c, n) @@ -1155,6 +1139,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = ty = n.sons[0].typ return nil 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}) var check: PNode = nil 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 # each interpolation. We'll pass this template to getAst. var - doBlk = n{-1} + quotedBlock = n{-1} op = if n.len == 3: expectString(c, n[1]) else: "``" quotes = newSeq[PNode](1) # the quotes will be added to a nkCall statement @@ -1721,20 +1707,23 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = ids = newSeq[PNode]() # this will store the generated param names - if doBlk.kind != nkDo: + if quotedBlock.kind != nkStmtList: 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: - doBlk.sons[paramsPos] = newNodeI(nkFormalParams, n.info) - doBlk[paramsPos].add getSysSym("typed").newSymNode # return type + dummyTemplate.sons[paramsPos] = newNodeI(nkFormalParams, n.info) + dummyTemplate[paramsPos].add getSysSym("typed").newSymNode # return type ids.add getSysSym("untyped").newSymNode # params type 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] result = newNode(nkCall, n.info, @[ getMagicSym(mExpandToAst).newSymNode, @@ -2158,10 +2147,6 @@ proc shouldBeBracketExpr(n: PNode): bool = n.sons[0] = be 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 = result = 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 nkBracket: result = semArrayConstr(c, n, flags) of nkObjConstr: result = semObjConstr(c, n, flags) - of nkLambda: result = semLambda(c, n, flags) - of nkDo: result = semDo(c, n, flags) + of nkLambdaKinds: result = semLambda(c, n, flags) of nkDerefExpr: result = semDeref(c, n) of nkAddr: result = n diff --git a/compiler/semfields.nim b/compiler/semfields.nim index 06826ef75..6002705b3 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -121,12 +121,13 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = localError(n.info, errWrongNumberOfVariables) 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}: localError(n.info, errGenerated, "no object or tuple type") return result 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): typeMismatch(call.sons[i].info, tupleTypeA, tupleTypeB) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 874be8dd6..b5dca4c1b 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -36,7 +36,8 @@ proc rawPushProcCon(c: PContext, owner: PSym) = c.p = x 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 if params.len > 1: let arg = params[1].sym diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 3e1989eaf..eb6259df0 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -89,9 +89,9 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode = proc toNode(t: PType, i: TLineInfo): PNode = result = newNodeIT(nkType, i, t) -const +const # these are types that use the bracket syntax for instantiation - # they can be subjected to the type traits `genericHead` and + # they can be subjected to the type traits `genericHead` and # `Uninstantiated` tyUserDefinedGenerics* = {tyGenericInst, tyGenericInvocation, tyUserTypeClassInst} @@ -109,27 +109,43 @@ proc uninstantiate(t: PType): PType = of tyCompositeTypeClass: uninstantiate t.sons[1] else: t -proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode = - var typ = operand.skipTypes({tyTypeDesc}) +proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode = + 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 + of "or", "|": + return typeWithSonsResult(tyOr, @[operand, operand2]) + of "and": + return typeWithSonsResult(tyAnd, @[operand, operand2]) + of "not": + return typeWithSonsResult(tyNot, @[operand]) of "name": - result = newStrNode(nkStrLit, typ.typeToString(preferName)) + result = newStrNode(nkStrLit, operand.typeToString(preferName)) result.typ = newType(tyString, context) - result.info = trait.info + result.info = traitCall.info 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.info = trait.info + result.info = traitCall.info of "genericHead": - var res = uninstantiate(typ) - if res == typ and res.kind notin tyMagicGenerics: - localError(trait.info, + var res = uninstantiate(operand) + if res == operand and res.kind notin tyMagicGenerics: + localError(traitCall.info, "genericHead expects a generic type. The given type was " & - typeToString(typ)) - return newType(tyError, context).toNode(trait.info) - result = res.base.toNode(trait.info) + typeToString(operand)) + return newType(tyError, context).toNode(traitCall.info) + result = res.base.toNode(traitCall.info) of "stripGenericParams": - result = uninstantiate(typ).toNode(trait.info) + result = uninstantiate(operand).toNode(traitCall.info) else: internalAssert false @@ -140,7 +156,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode = if t.sonsLen > 0: # This is either a type known to sem or a typedesc # param to a regular proc (again, known at instantiation) - result = evalTypeTrait(n[0], t, getCurrOwner(c)) + result = evalTypeTrait(n, t, getCurrOwner(c)) else: # a typedesc variable, pass unmodified to evals result = n diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index be0d90c0f..8f522ccc2 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -730,7 +730,9 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = var s: PSym if name.kind == nkDotExpr: 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") else: s = semIdentDef(c, name, skType) @@ -742,6 +744,87 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = if sfGenSym notin s.flags: addInterfaceDecl(c, 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) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] @@ -782,6 +865,22 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = body.sym = s body.size = -1 # could not be computed properly 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) closeScope(c) elif a.sons[2].kind != nkEmpty: @@ -851,9 +950,12 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = # type aliases are hard: var t = semTypeNode(c, x, nil) assert t != nil - if t.kind in {tyObject, tyEnum, tyDistinct}: - assert s.typ != nil - if s.typ.kind != tyAlias: + 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 assignType(s.typ, t) s.typ.id = t.id # same id checkConstructedType(s.info, s.typ) @@ -1066,13 +1168,6 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = popOwner(c) 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 = var n = n @@ -1177,7 +1272,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = var objB = t.sons[2] while true: 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 if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB): if obj.assignment.isNil: @@ -1666,7 +1762,11 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = else: discard 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 = result.sons[0] diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 422d2f0fa..09f90d8d0 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -135,7 +135,9 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = let isCall = ord(n.kind in nkCallKinds+{nkBracketExpr}) let n = if n[0].kind == nkBracket: n[0] else: n 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) var isNilable = false # 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}: message n[i].info, errGenerated, "region needs to be an object type" addSonSkipIntLit(result, region) - addSonSkipIntLit(result, base) + addSonSkipIntLit(result, t) #if not isNilable: result.flags.incl tfNotNil 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]) 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, allowMetaTypes = true) result = liftingWalk(expanded, true) - of tyUserTypeClasses, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: - result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), true)) + of tyUserTypeClasses, tyBuiltInTypeClass, tyCompositeTypeClass, + tyAnd, tyOr, tyNot: + result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), false)) of tyGenericParam: 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 = result = semTypeNode(c, n, nil) + n.typ = makeTypeDesc(c, result) proc semObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType) = var @@ -1574,10 +1584,24 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = # type for each generic param. the index # of the parameter will be stored in the # 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: - newSymG(skGenericParam, a.sons[j], c).linkTo(finalType) + newSymG(skGenericParam, paramName, c).linkTo(finalType) 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 father != nil: addSonSkipIntLit(father, s.typ) s.position = result.len diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 9ff0b7e78..80fb9168b 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -9,10 +9,10 @@ # 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 - tfInstClearedFlags = {tfHasMeta} + tfInstClearedFlags = {tfHasMeta, tfUnresolved} proc checkPartialConstructedType(info: TLineInfo, t: PType) = 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? # See the notes for PActor in handleGenericInvocation return + if not sameFlags(inst, key): + continue + block matchType: for j in 1 .. high(key.sons): # 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 = # XXX: relying on allowMetaTypes is a kludge result = copyType(t, t.owner, cl.allowMetaTypes) + if cl.allowMetaTypes: return result.flags.incl tfFromGeneric if not (t.kind in tyMetaTypes or (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)) else: result = searchInstTypes(t) + if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return for i in countup(1, sonsLen(t) - 1): var x = t.sons[i] - if x.kind == tyGenericParam: + if x.kind in {tyGenericParam}: x = lookupTypeVar(cl, x) if x != nil: if header == t: header = instCopyType(cl, t) @@ -307,31 +312,32 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = rawAddSon(result, newbody) checkPartialConstructedType(cl.info, newbody) let dc = newbody.deepCopy - if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: - # 'deepCopy' needs to be instantiated for - # generics *when the type is constructed*: - newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, - attachedDeepCopy, 1) - if bodyIsNew and newbody.typeInst == nil: - #doassert newbody.typeInst == nil - newbody.typeInst = result - if tfRefsAnonObj in newbody.flags and newbody.kind != tyGenericInst: - # can come here for tyGenericInst too, see tests/metatype/ttypeor.nim - # need to look into this issue later - assert newbody.kind in {tyRef, tyPtr} - assert newbody.lastSon.typeInst == nil - newbody.lastSon.typeInst = result - let asgn = newbody.assignment - if asgn != nil and sfFromGeneric notin asgn.flags: - # '=' needs to be instantiated for generics when the type is constructed: - newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info, - attachedAsgn, 1) - let methods = skipTypes(bbody, abstractPtrs).methods - for col, meth in items(methods): - # we instantiate the known methods belonging to that type, this causes - # them to be registered and that's enough, so we 'discard' the result. - discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info, - attachedAsgn, col) + if cl.allowMetaTypes == false: + if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: + # 'deepCopy' needs to be instantiated for + # generics *when the type is constructed*: + newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, + attachedDeepCopy, 1) + if bodyIsNew and newbody.typeInst == nil: + #doassert newbody.typeInst == nil + newbody.typeInst = result + if tfRefsAnonObj in newbody.flags and newbody.kind != tyGenericInst: + # can come here for tyGenericInst too, see tests/metatype/ttypeor.nim + # need to look into this issue later + assert newbody.kind in {tyRef, tyPtr} + assert newbody.lastSon.typeInst == nil + newbody.lastSon.typeInst = result + let asgn = newbody.assignment + if asgn != nil and sfFromGeneric notin asgn.flags: + # '=' needs to be instantiated for generics when the type is constructed: + newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info, + attachedAsgn, 1) + let methods = skipTypes(bbody, abstractPtrs).methods + for col, meth in items(methods): + # we instantiate the known methods belonging to that type, this causes + # them to be registered and that's enough, so we 'discard' the result. + discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info, + attachedAsgn, col) proc eraseVoidParams*(t: PType) = # transform '(): void' into '()' because old parts of the compiler really @@ -526,6 +532,14 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, result = replaceTypeVarsT(cl, t) 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, t: PType): untyped = generateTypeInstance(p, pt, arg.info, t) diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index fd703a433..6eecf98d8 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -154,7 +154,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = else: c.hashSym(t.sym) return - of tyAlias, tyGenericInst: + of tyAlias, tyGenericInst, tyUserTypeClasses: c.hashType t.lastSon, flags return else: @@ -201,16 +201,6 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = of tyRef, tyPtr, tyGenericBody, tyVar: c.hashType t.lastSon, flags 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: c.hashTree(t.n) of tyTuple: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4661abda0..cb526947e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -26,7 +26,7 @@ type sym*: PSym unmatchedVarParam*: int diagnostics*: seq[string] - + CandidateErrors* = seq[CandidateError] TCandidate* = object @@ -68,7 +68,13 @@ type # future. mutabilityProblem*: uint8 # tyVar mismatch inheritancePenalty: int # to prefer closest father object type - + + TTypeRelFlag* = enum + trDontBind + trNoCovariance + + TTypeRelFlags* = set[TTypeRelFlag] + TTypeRelation* = enum # order is important! isNone, isConvertible, isIntConv, @@ -177,6 +183,13 @@ proc sumGeneric(t: PType): int = tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody: t = t.lastSon 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: t = t.sons[0] inc result @@ -185,8 +198,8 @@ proc sumGeneric(t: PType): int = t = t.lastSon if t.kind == tyEmpty: break inc result - of tyGenericInvocation, tyTuple, tyProc: - result += ord(t.kind == tyGenericInvocation) + of tyGenericInvocation, tyTuple, tyProc, tyAnd: + result += ord(t.kind in {tyGenericInvocation, tyAnd}) for i in 0 .. <t.len: if t.sons[i] != nil: 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 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 = result = a.exactMatches - b.exactMatches if result != 0: return @@ -248,14 +270,6 @@ proc cmpCandidates*(a, b: TCandidate): int = if result != 0: return 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 = if arg.kind in nkSymChoices: result = typeToString(arg[0].typ, prefer) @@ -288,7 +302,9 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1; add(result, argTypeToString(arg, prefer)) 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 = case t.kind of tyNil: @@ -639,7 +655,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, makeTypeDesc(c, typ) typeParams.safeAdd((param, typ)) - + addDecl(c, param) for param in typeClass.n[0]: @@ -676,7 +692,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, flags: TExprFlags = {} collectDiagnostics = m.diagnostics != nil or sfExplain in typeClass.sym.flags - + if collectDiagnostics: oldWriteHook = writelnHook # XXX: we can't write to m.diagnostics directly, because @@ -688,13 +704,13 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, let msg = s.replace("Error:", errorPrefix) if oldWriteHook != nil: oldWriteHook msg diagnostics.add msg - + var checkedBody = c.semTryExpr(c, body.copyTree, flags) if collectDiagnostics: writelnHook = oldWriteHook for msg in diagnostics: m.diagnostics.safeAdd msg - + if checkedBody == nil: return nil # The inferrable type params have been identified during the semTryExpr above. @@ -739,14 +755,14 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode, allowMetaTypes = allowUnresolved) 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, # capable of deriving the value of a static parameter in # expressions such as (N + 5) / 2 = rhs # # Preconditions: # - # * The input of this proc must be semantized + # * The input of this proc must be semantized # - all templates should be expanded # - aby constant folding possible should already be performed # @@ -754,64 +770,69 @@ proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType = # # Result: # - # The proc will return the inferred static type with the `n` field - # populated with the inferred value. - # - # `nil` will be returned if the inference was not possible + # The proc will return true if the static types was successfully + # inferred. The result will be bound to the original static type + # in the TCandidate. # if lhs.kind in nkCallKinds and lhs[0].kind == nkSym: case lhs[0].sym.magic of mUnaryLt: - return inferStaticParam(lhs[1], rhs + 1) + return inferStaticParam(c, lhs[1], rhs + 1) of mAddI, mAddU, mInc, mSucc: 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: - return inferStaticParam(lhs[1], rhs - lhs[2].intVal) - + return inferStaticParam(c, lhs[1], rhs - lhs[2].intVal) + of mDec, mSubI, mSubU, mPred: 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: - return inferStaticParam(lhs[1], rhs + lhs[2].intVal) - + return inferStaticParam(c, lhs[1], rhs + lhs[2].intVal) + of mMulI, mMulU: if lhs[1].kind == nkIntLit: 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: 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: if lhs[1].kind == nkIntLit: 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: - return inferStaticParam(lhs[1], lhs[2].intVal * rhs) - + return inferStaticParam(c, lhs[1], lhs[2].intVal * rhs) + of mShlI: 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: 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: - return inferStaticParam(lhs[1], -rhs) - + return inferStaticParam(c, lhs[1], -rhs) + of mUnaryPlusI, mToInt, mToBiggestInt: - return inferStaticParam(lhs[1], rhs) - + return inferStaticParam(c, lhs[1], rhs) + else: discard - + elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil: - lhs.typ.n = newIntNode(nkIntLit, rhs) - return lhs.typ - - return nil + var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ.sons) + 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 false proc failureToInferStaticParam(n: PNode) = let staticParam = n.findUnresolvedStatic @@ -825,13 +846,10 @@ proc inferStaticsInRange(c: var TCandidate, allowUnresolved = true) let upperBound = tryResolvingStaticExpr(c, inferred.n[1], allowUnresolved = true) - - template doInferStatic(c: var TCandidate, e: PNode, r: BiggestInt) = + template doInferStatic(e: PNode, r: BiggestInt) = var exp = e var rhs = r - var inferred = inferStaticParam(exp, rhs) - if inferred != nil: - put(c, inferred, inferred) + if inferStaticParam(c, exp, rhs): return isGeneric else: failureToInferStaticParam exp @@ -842,15 +860,36 @@ proc inferStaticsInRange(c: var TCandidate, return isGeneric else: return isNone - doInferStatic(c, upperBound, lengthOrd(concrete) + lowerBound.intVal - 1) + doInferStatic(upperBound, lengthOrd(concrete) + lowerBound.intVal - 1) elif upperBound.kind == nkIntLit: - doInferStatic(c, lowerBound, upperBound.intVal + 1 - lengthOrd(concrete)) + doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(concrete)) template subtypeCheck() = if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}: 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: # # 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 return + template doBind: bool = trDontBind notin flags + # var and static arguments match regular modifier-free types var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym) # 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 of tyOr: + # XXX: deal with the current dual meaning of tyGenericParam + c.typedescMatched = true # seq[int|string] vs seq[number] # both int and string must match against number # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219): result = isGeneric 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 < result: result = x + return of tyAnd: + # XXX: deal with the current dual meaning of tyGenericParam + c.typedescMatched = true # seq[Sortable and Iterable] vs seq[Sortable] # only one match is enough for branch in a.sons: - let x = typeRel(c, f, branch, false) + let x = typeRel(c, f, branch, flags + {trDontBind}) if x != isNone: return if x >= isGeneric: isGeneric else: x - result = isNone + return isNone of tyNot: case f.kind @@ -986,7 +1032,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyUserTypeClass, tyUserTypeClassInst: # consider this: 'var g: Node' *within* a concept where 'Node' # is a concept too (tgraph) - let x = typeRel(c, a, f, false) + let x = typeRel(c, a, f, flags + {trDontBind}) if x >= isGeneric: return isGeneric else: discard @@ -1032,7 +1078,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyFloat128: result = handleFloatRange(f, a) of tyVar: 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() of tyArray: case a.kind @@ -1046,38 +1092,60 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = fRange = a else: fRange = prev - result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}), - a.sons[1].skipTypes({tyTypeDesc})) - if result < isGeneric: return isNone - + let ff = f.sons[1].skipTypes({tyTypeDesc}) + let aa = a.sons[1].skipTypes({tyTypeDesc}) + 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: return inferStaticsInRange(c, fRange, a) elif c.c.inTypeClass > 0 and aRange.rangeHasUnresolvedStatic: return inferStaticsInRange(c, aRange, f) - elif lengthOrd(fRange) != lengthOrd(a): - result = isNone + else: + if lengthOrd(fRange) != lengthOrd(aRange): + result = isNone else: discard of tyOpenArray, tyVarargs: # varargs[expr] is special too but handled earlier. So we only need to # handle varargs[stmt] which is the same as varargs[typed]: if f.kind == tyVarargs: + if tfVarargs in a.flags: + return typeRel(c, f.base, a.lastSon) if tfOldSchoolExprStmt in f.sons[0].flags: if f.sons[0].kind == tyExpr: 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 of tyOpenArray, tyVarargs: result = typeRel(c, base(f), base(a)) if result < isGeneric: result = isNone of tyArray: if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): - result = isSubtype - elif typeRel(c, base(f), a.sons[1]) >= isGeneric: - result = isConvertible + return isSubtype + matchArrayOrSeq(a.sons[1]) of tySequence: if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): - result = isConvertible - elif typeRel(c, base(f), a.sons[0]) >= isGeneric: - result = isConvertible + return isConvertible + matchArrayOrSeq(a.sons[0]) of tyString: if f.kind == tyOpenArray: 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): result = isSubtype else: - result = typeRel(c, f.sons[0], a.sons[0]) - if result < isGeneric: result = isNone + let ff = f.sons[0] + 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: result = isNilConversion 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 for i in 0..f.len-2: 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() if result <= isConvertible: result = isNone 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: if a.kind == f.kind: result = isEqual - of tyGenericInst, tyAlias: + of tyAlias: 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: considerPreviousT: 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: 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 if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody: #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation") @@ -1333,13 +1474,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if f.isResolvedUserTypeClass: result = typeRel(c, f.lastSon, a) else: - var matched = matchUserTypeClass(c.c, c, f, aOrig) - if matched != nil: - bindConcreteTypeToUserTypeClass(matched, a) - put(c, f, matched) - result = isGeneric - else: - result = isNone + considerPreviousT: + var matched = matchUserTypeClass(c.c, c, f, aOrig) + if matched != nil: + bindConcreteTypeToUserTypeClass(matched, a) + if doBind: put(c, f, matched) + result = isGeneric + else: + result = isNone of tyCompositeTypeClass: considerPreviousT: @@ -1357,6 +1499,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if result != isNone: put(c, f, a) result = isGeneric + of tyGenericParam: var x = PType(idTableGet(c.bindings, f)) 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 c.typedescMatched = true var aa = a - while aa.kind in {tyTypeDesc, tyGenericParam} and - aa.len > 0: + while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0: aa = lastSon(aa) + if aa.kind == tyGenericParam: + return isGeneric result = typeRel(c, f.base, aa) if result > isGeneric: result = isGeneric else: result = isNone else: 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}: let concrete = concreteType(c, a) if concrete == nil: return isNone @@ -1590,6 +1734,10 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) = of isEqual: inc(m.exactMatches) 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, argSemantized, argOrig: PNode): PNode = var @@ -1726,6 +1874,14 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, inc(m.genericMatches) m.fauxMatch = a.kind 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) # check for a base type match, which supports varargs[T] without [] # constructor in a call: @@ -2008,6 +2164,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, #assert(container == nil) if container.isNil: container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) + container.typ.flags.incl tfVarargs else: incrIndexType(container.typ) 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 & "'") return nil var f = dc.typ.sons[col] + if op == attachedDeepCopy: if f.kind in {tyRef, tyPtr}: f = f.lastSon else: diff --git a/compiler/types.nim b/compiler/types.nim index 3f84548a1..2886ac619 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -886,6 +886,9 @@ proc isGenericAlias*(t: PType): bool = proc skipGenericAlias*(t: PType): PType = 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 = template cycleCheck() = # 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: 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 var a = skipTypes(x, {tyGenericInst, tyAlias}) var b = skipTypes(y, {tyGenericInst, tyAlias}) diff --git a/compiler/vm.nim b/compiler/vm.nim index 3c475cf57..e201e98dc 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1606,6 +1606,13 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg = n.typ = x.typ 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 proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, diff --git a/doc/manual/type_rel.txt b/doc/manual/type_rel.txt index 5b68f73aa..1d1425934 100644 --- a/doc/manual/type_rel.txt +++ b/doc/manual/type_rel.txt @@ -111,6 +111,105 @@ relation is extended to the types ``var``, ``ref``, ``ptr``: .. 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 -------------------- A type ``a`` is **implicitly** convertible to type ``b`` iff the following @@ -119,6 +218,8 @@ algorithm returns true: .. code-block:: nim # XXX range types? proc isImplicitlyConvertible(a, b: PType): bool = + if isSubtype(a, b) or isCovariant(a, b): + return true case a.kind of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float, float32, float64} diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 646b79fe5..3b3f38e41 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -78,6 +78,10 @@ type ## Statically typed wrapper around a JavaScript object. NotString = concept c c isnot string + js* = JsObject + +var jsarguments* {.importc: "arguments", nodecl}: JsObject + ## JavaScript's arguments pseudo-variable # New proc newJsObject*: JsObject {. importcpp: "{@}" .} @@ -93,18 +97,64 @@ proc hasOwnProperty*(x: JsObject, prop: cstring): bool proc jsTypeOf*(x: JsObject): cstring {. importcpp: "typeof(#)" .} ## 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 proc to*(x: JsObject, T: typedesc): T {. importcpp: "(#)" .} ## Converts a JsObject `x` to type `T`. + proc toJs*[T](val: T): JsObject {. importcpp: "(#)" .} ## 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 .} ## 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 .} ## 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 {. importcpp: getImpl .} ## 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 .} helper(`obj`, `value`) -macro `.()`*(obj: JsObject, field: static[cstring], - args: varargs[JsObject, toJs]): JsObject = +macro `.()`*(obj: JsObject, + field: static[cstring], + args: varargs[JsObject, jsFromAst]): JsObject = ## Experimental "method call" operator for type JsObject. ## 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, @@ -196,9 +247,9 @@ macro `.()`*(obj: JsObject, field: static[cstring], if not mangledNames.hasKey($field): mangledNames[$field] = $mangleJsName(field) importString = "#." & mangledNames[$field] & "(@)" - result = quote do: + result = quote: proc helper(o: JsObject): JsObject - {. importcpp: `importString`, gensym .} + {. importcpp: `importString`, gensym, discardable .} helper(`obj`) for idx in 0 ..< args.len: let paramName = newIdentNode(!("param" & $idx)) @@ -206,7 +257,7 @@ macro `.()`*(obj: JsObject, field: static[cstring], result[1].add args[idx].copyNimTree macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V], - field: static[cstring]): V = + field: static[cstring]): V = ## Experimental dot accessor (get) for type JsAssoc. ## Returns the value of a property of name `field` from a JsObject `x`. var importString: string @@ -222,7 +273,8 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V], helper(`obj`) 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. ## Sets the value of a property of name `field` in a JsObject `x` to `value`. var importString: string @@ -238,7 +290,8 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V], helper(`obj`, `value`) 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. ## 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 diff --git a/lib/system.nim b/lib/system.nim index 9b41253cc..05b4b88f2 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -49,6 +49,9 @@ type cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr`` ## operator to get a pointer to a variable + + typedesc* {.magic: TypeDesc.} ## meta type to denote a type description + const on* = true ## alias for ``true`` off* = false ## alias for ``false`` @@ -56,6 +59,15 @@ const {.push warning[GcMem]: off, warning[Uninit]: 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 Ordinal* {.magic: Ordinal.}[T] ## Generic ordinal type. Includes integer, ## bool, character, and enumeration types @@ -66,11 +78,11 @@ type `ref`* {.magic: Pointer.}[T] ## built-in generic traced pointer type `nil` {.magic: "Nil".} + 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) ## **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 auto* {.magic: Expr.} ## meta type for automatic type determination any* = distinct auto ## meta type for any supported type @@ -1332,6 +1344,7 @@ const hasThreadSupport = compileOption("threads") and not defined(nimscript) hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own taintMode = compileOption("taintmode") + nimEnableCovariance* = defined(nimEnableCovariance) # or true when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"): # tcc doesn't support TLS diff --git a/tests/clearmsg/tmacroerrorproc.nim b/tests/clearmsg/tmacroerrorproc.nim index 9a6ff6a06..cd9b15e25 100644 --- a/tests/clearmsg/tmacroerrorproc.nim +++ b/tests/clearmsg/tmacroerrorproc.nim @@ -7,7 +7,7 @@ discard """ import macros macro mixer(n: typed): untyped = - expectKind(n, nnkCharLit) - + expectKind(n[0], nnkCharLit) + mixer: - echo "owh" \ No newline at end of file + echo "owh" diff --git a/tests/closure/tdonotation.nim b/tests/closure/tdonotation.nim new file mode 100644 index 000000000..94eba8ddb --- /dev/null +++ b/tests/closure/tdonotation.nim @@ -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") + diff --git a/tests/concepts/t5642.nim b/tests/concepts/t5642.nim new file mode 100644 index 000000000..d1e7bd1dd --- /dev/null +++ b/tests/concepts/t5642.nim @@ -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) + diff --git a/tests/concepts/tconceptinclosure.nim b/tests/concepts/tconceptinclosure.nim new file mode 100644 index 000000000..23c1bf293 --- /dev/null +++ b/tests/concepts/tconceptinclosure.nim @@ -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() + diff --git a/tests/concepts/tmisc_issues.nim b/tests/concepts/tmisc_issues.nim index 10e072521..d9bb84a2f 100644 --- a/tests/concepts/tmisc_issues.nim +++ b/tests/concepts/tmisc_issues.nim @@ -42,7 +42,7 @@ echo p2 is AbstractPointOfFloat # true echo p2.x is float and p2.y is float # true # https://github.com/nim-lang/Nim/issues/2018 -type ProtocolFollower = generic +type ProtocolFollower = concept true # not a particularly involved protocol type ImplementorA = object diff --git a/tests/concepts/trandom_vars.nim b/tests/concepts/trandom_vars.nim new file mode 100644 index 000000000..a236cebad --- /dev/null +++ b/tests/concepts/trandom_vars.nim @@ -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) diff --git a/tests/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim new file mode 100644 index 000000000..dfe4cb941 --- /dev/null +++ b/tests/cpp/tcovariancerules.nim @@ -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) + diff --git a/tests/errmsgs/tinvalidinout.nim b/tests/errmsgs/tinvalidinout.nim new file mode 100644 index 000000000..ce7eb6022 --- /dev/null +++ b/tests/errmsgs/tinvalidinout.nim @@ -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 + diff --git a/tests/generics/t5570.nim b/tests/generics/t5570.nim new file mode 100644 index 000000000..e3f9ff415 --- /dev/null +++ b/tests/generics/t5570.nim @@ -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 + diff --git a/tests/generics/t5602_inheritence.nim b/tests/generics/t5602_inheritence.nim new file mode 100644 index 000000000..6d48c796e --- /dev/null +++ b/tests/generics/t5602_inheritence.nim @@ -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 + diff --git a/tests/generics/t5643.nim b/tests/generics/t5643.nim new file mode 100644 index 000000000..962d5cef5 --- /dev/null +++ b/tests/generics/t5643.nim @@ -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. + diff --git a/tests/generics/t5683.nim b/tests/generics/t5683.nim new file mode 100644 index 000000000..38da52ec2 --- /dev/null +++ b/tests/generics/t5683.nim @@ -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]) + diff --git a/tests/generics/tbindoncevsbindmany.nim b/tests/generics/tbindoncevsbindmany.nim new file mode 100644 index 000000000..01e801f0e --- /dev/null +++ b/tests/generics/tbindoncevsbindmany.nim @@ -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) + diff --git a/tests/generics/tfakecovariance.nim b/tests/generics/tfakecovariance.nim new file mode 100644 index 000000000..0920cb504 --- /dev/null +++ b/tests/generics/tfakecovariance.nim @@ -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) + diff --git a/tests/generics/tgenericconst.nim b/tests/generics/tgenericconst.nim new file mode 100644 index 000000000..3c86888df --- /dev/null +++ b/tests/generics/tgenericconst.nim @@ -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() + diff --git a/tests/generics/tmapping_generic_alias.nim b/tests/generics/tmapping_generic_alias.nim new file mode 100644 index 000000000..efdf32ead --- /dev/null +++ b/tests/generics/tmapping_generic_alias.nim @@ -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 + diff --git a/tests/generics/tparam_binding.nim b/tests/generics/tparam_binding.nim new file mode 100644 index 000000000..643e9b226 --- /dev/null +++ b/tests/generics/tparam_binding.nim @@ -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) + diff --git a/tests/generics/tptrinheritance.nim b/tests/generics/tptrinheritance.nim new file mode 100644 index 000000000..221b8777b --- /dev/null +++ b/tests/generics/tptrinheritance.nim @@ -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) + diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim index 71eb211e3..e4aad4b99 100644 --- a/tests/js/tjsffi.nim +++ b/tests/js/tjsffi.nim @@ -1,5 +1,5 @@ discard """ - output: '''true +output: ''' true true true @@ -14,10 +14,18 @@ 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 # Test JsObject []= and [] @@ -55,8 +63,8 @@ block: block: proc test(): bool = let obj = newJsObject() - obj.`?!$` = proc(x, y, z: int, t: string): string = t & $(x + y + z) - obj.`?!$`(1, 2, 3, "Result is: ").to(string) == "Result is: 6" + obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z) + obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == "Result is: 6" echo test() # Test JsObject []() @@ -265,3 +273,47 @@ block: let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever)) obj.onWhatever(1) == 10 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] + diff --git a/tests/macros/tlexerex.nim b/tests/macros/tlexerex.nim index d348a4bcc..db2c38ef6 100644 --- a/tests/macros/tlexerex.nim +++ b/tests/macros/tlexerex.nim @@ -1,8 +1,7 @@ - import macros -macro match*(s: cstring|string; pos: int; sections: untyped): untyped = - for sec in sections.children: +macro match*(s: cstring|string; pos: int; sections: varargs[untyped]): untyped = + for sec in sections: expectKind sec, nnkOfBranch expectLen sec, 2 result = newStmtList() diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim new file mode 100644 index 000000000..85f2628bf --- /dev/null +++ b/tests/parser/tpostexprblocks.nim @@ -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]) + diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index b05cb5a10..6a53a2964 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -1,7 +1,10 @@ discard """ output: '''{"age": 12, "bio": "\u042F Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"} true -true''' +true +alpha 100 +omega 200 +''' """ import marshal @@ -83,3 +86,22 @@ var instance1 = Person(name: "Cletus", age: 12, echo($$instance1) echo(to[Person]($$instance1).bio == instance1.bio) 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 + diff --git a/tests/template/tgenerictemplates.nim b/tests/template/tgenerictemplates.nim new file mode 100644 index 000000000..2c83bc0ec --- /dev/null +++ b/tests/template/tgenerictemplates.nim @@ -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]() + diff --git a/tests/template/tgensymregression.nim b/tests/template/tgensymregression.nim index 0fadbde41..4cc64a831 100644 --- a/tests/template/tgensymregression.nim +++ b/tests/template/tgensymregression.nim @@ -59,14 +59,13 @@ template wrap(body: typed): untyped = macro makeProc(): typed = # Make a template tree - result = (quote do: + result = quote do: proc someProc* = wrap do: let x = 123 # Implicit conversion here let s: string = x echo s - ) makeProc() diff --git a/tests/types/t5640.nim b/tests/types/t5640.nim new file mode 100644 index 000000000..5e1c99c4d --- /dev/null +++ b/tests/types/t5640.nim @@ -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]) + diff --git a/tests/types/t5648.nim b/tests/types/t5648.nim new file mode 100644 index 000000000..c230cc12c --- /dev/null +++ b/tests/types/t5648.nim @@ -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() + diff --git a/tests/types/thard_tyforward.nim b/tests/types/thard_tyforward.nim new file mode 100644 index 000000000..7131cd64b --- /dev/null +++ b/tests/types/thard_tyforward.nim @@ -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] + diff --git a/web/bountysource.nim b/web/bountysource.nim index 1d47cea56..5dfdb4497 100644 --- a/web/bountysource.nim +++ b/web/bountysource.nim @@ -33,13 +33,13 @@ proc getSupporters(self: BountySource): Future[JsonNode] {.async.} = let response = await self.client.get(apiUrl & "/supporters?order=monthly&per_page=200&team_slug=" & self.team) doAssert response.status.startsWith($Http200) - return parseJson(response.body) + return parseJson(await response.body) proc getGithubUser(username: string): Future[JsonNode] {.async.} = let client = newAsyncHttpClient() let response = await client.get(githubApiUrl & "/users/" & username) if response.status.startsWith($Http200): - return parseJson(response.body) + return parseJson(await response.body) else: echo("Could not get Github user: ", username, ". ", response.status) return nil |