diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 29 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 4 | ||||
-rw-r--r-- | compiler/ccgstmts.nim | 2 | ||||
-rw-r--r-- | compiler/ccgthreadvars.nim | 4 | ||||
-rw-r--r-- | compiler/ccgtrav.nim | 8 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 3 | ||||
-rw-r--r-- | compiler/cgen.nim | 19 | ||||
-rw-r--r-- | compiler/cgendata.nim | 4 | ||||
-rw-r--r-- | compiler/lambdalifting.nim | 7 | ||||
-rw-r--r-- | compiler/parser.nim | 20 | ||||
-rw-r--r-- | compiler/semdata.nim | 5 | ||||
-rw-r--r-- | compiler/semexprs.nim | 17 | ||||
-rw-r--r-- | compiler/seminst.nim | 132 | ||||
-rw-r--r-- | compiler/semstmts.nim | 3 | ||||
-rw-r--r-- | compiler/semtypes.nim | 292 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 64 | ||||
-rw-r--r-- | compiler/types.nim | 79 |
17 files changed, 457 insertions, 235 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 514ab6a7c..bb015ea27 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -366,12 +366,8 @@ type tfFromGeneric, # type is an instantiation of a generic; this is needed # because for instantiations of objects, structural # type equality has to be used - tfInstantiated, # XXX: used to mark generic params after instantiation. - # if the concrete type happens to be an implicit generic - # this can lead to invalid proc signatures in the second - # pass of semProcTypeNode performed after instantiation. - # this won't be needed if we don't perform this redundant - # second pass (stay tuned). + tfUnresolved, # marks unresolved typedesc params: e.g. + # proc foo(T: typedesc, list: seq[T]): var T tfRetType, # marks return types in proc (used to detect type classes # used as return types for return type inference) tfAll, # type class requires all constraints to be met (default) @@ -690,6 +686,8 @@ type # for record types a nkRecord node # for enum types a list of symbols # for tyInt it can be the int literal + # for procs and tyGenericBody, it's the + # formal param list # else: unused destructor*: PSym # destructor. warning: nil here may not necessary # mean that there is no destructor. @@ -794,6 +792,7 @@ const nkStrKinds* = {nkStrLit..nkTripleStrLit} skLocalVars* = {skVar, skLet, skForVar, skParam, skResult} + skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skMethod, skConverter} lfFullExternalName* = lfParamCopy # \ # only used when 'gCmd == cmdPretty': Indicates that the symbol has been @@ -1053,8 +1052,8 @@ proc NewType(kind: TTypeKind, owner: PSym): PType = result.size = - 1 result.align = 2 # default alignment result.id = getID() - when debugIds: - RegisterId(result) + when debugIds: + RegisterId(result) #if result.id < 2000 then # MessageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id)) @@ -1091,7 +1090,6 @@ proc copyType(t: PType, owner: PSym, keepId: bool): PType = if keepId: result.id = t.id else: - result.id = getID() when debugIds: RegisterId(result) result.sym = t.sym # backend-info should not be copied @@ -1361,10 +1359,19 @@ proc getStrOrChar*(a: PNode): string = proc isGenericRoutine*(s: PSym): bool = case s.kind - of skProc, skTemplate, skMacro, skIterator, skMethod, skConverter: - result = s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty + of skProcKinds: + result = sfFromGeneric in s.flags or + (s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty) else: nil +proc skipGenericOwner*(s: PSym): PSym = + InternalAssert s.kind in skProcKinds + ## Generic instantiations are owned by their originating generic + ## symbol. This proc skips such owners and goes straigh to the owner + ## of the generic itself (the module or the enclosing proc). + result = if sfFromGeneric in s.flags: s.owner.owner + else: s.owner + proc isRoutine*(s: PSym): bool {.inline.} = result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod, skConverter} diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index bb1035ab6..635756220 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1932,12 +1932,12 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = nil of nkPragma: genPragma(p, n) of nkProcDef, nkMethodDef, nkConverterDef: - if (n.sons[genericParamsPos].kind == nkEmpty): + if (n.sons[genericParamsPos].kind == nkEmpty): var prc = n.sons[namePos].sym # due to a bug/limitation in the lambda lifting, unused inner procs # are not transformed correctly. We work around this issue (#411) here # by ensuring it's no inner proc (owner is a module): - if prc.owner.kind == skModule: + if prc.skipGenericOwner.kind == skModule: if (optDeadCodeElim notin gGlobalOptions and sfDeadCodeElim notin getModule(prc).flags) or ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 01c734524..6f362a615 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -152,7 +152,7 @@ proc genSingleVar(p: BProc, a: PNode) = var immediateAsgn = a.sons[2].kind != nkEmpty if sfGlobal in v.flags: if v.owner.kind != skModule: - targetProc = p.module.preInitProc + targetProc = p.module.postInitProc assignGlobalVar(targetProc, v) # XXX: be careful here. # Global variables should not be zeromem-ed within loops diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index 4785402e7..d312ea027 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -12,10 +12,10 @@ # included from cgen.nim -proc emulatedThreadVars(): bool {.inline.} = +proc emulatedThreadVars(): bool = result = {optThreads, optTlsEmulation} <= gGlobalOptions -proc AccessThreadLocalVar(p: BProc, s: PSym) = +proc accessThreadLocalVar(p: BProc, s: PSym) = if emulatedThreadVars() and not p.ThreadVarAccessed: p.ThreadVarAccessed = true p.module.usesThreadVars = true diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index aa8b85600..9534eae91 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -127,18 +127,22 @@ proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): PRope = m.s[cfsProcHeaders].appf("$1;$n", header) m.s[cfsProcs].app(generatedProc) - proc genTraverseProcForGlobal(m: BModule, s: PSym): PRope = discard genTypeInfo(m, s.loc.t) var c: TTraversalClosure var p = newProc(nil, m) + var sLoc = s.loc.r result = getGlobalTempName() + if sfThread in s.flags and emulatedThreadVars(): + accessThreadLocalVar(p, s) + sLoc = con("NimTV->", sLoc) + c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n" c.p = p let header = ropef("N_NIMCALL(void, $1)()", result) - genTraverseProc(c, s.loc.r, s.loc.t) + genTraverseProc(c, sLoc, s.loc.t) let generatedProc = ropef("$1 {$n$2$3$4}$n", [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index bb9d4c3a1..4548ac641 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -437,8 +437,9 @@ proc genRecordFieldsAux(m: BModule, n: PNode, app(result, genRecordFieldsAux(m, k, ae, rectype, check)) else: internalError("genRecordFieldsAux(record case branch)") appf(result, "} $1;$n", [uname]) - of nkSym: + of nkSym: field = n.sym + if field.typ.kind == tyEmpty: return #assert(field.ast == nil) sname = mangleRecFieldName(field, rectype) if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname]) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 18f33f32a..0c3f2da84 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -284,6 +284,9 @@ proc genLineDir(p: BProc, t: PNode) = linefmt(p, cpsStmts, "nimln($1, $2);$n", line.toRope, t.info.quotedFilename) +proc accessThreadLocalVar(p: BProc, s: PSym) +proc emulatedThreadVars(): bool {.inline.} + include "ccgtypes.nim" # ------------------------------ Manager of temporaries ------------------ @@ -858,8 +861,8 @@ proc isActivated(prc: PSym): bool = prc.typ != nil proc genProc(m: BModule, prc: PSym) = if sfBorrow in prc.flags or not isActivated(prc): return fillProcLoc(prc) - if {sfForward, sfFromGeneric} * prc.flags != {}: addForwardedProc(m, prc) - else: + if sfForward in prc.flags: addForwardedProc(m, prc) + else: genProcNoForward(m, prc) if {sfExportc, sfCompilerProc} * prc.flags == {sfExportc} and generatedHeader != nil and lfNoDecl notin prc.loc.Flags: @@ -1028,8 +1031,9 @@ proc genInitCode(m: BModule) = app(prc, initGCFrame(m.initProc)) app(prc, genSectionStart(cpsLocals)) - app(prc, m.initProc.s(cpsLocals)) app(prc, m.preInitProc.s(cpsLocals)) + app(prc, m.initProc.s(cpsLocals)) + app(prc, m.postInitProc.s(cpsLocals)) app(prc, genSectionEnd(cpsLocals)) if optStackTrace in m.initProc.options and not m.FrameDeclared: @@ -1045,11 +1049,13 @@ proc genInitCode(m: BModule) = app(prc, genSectionStart(cpsInit)) app(prc, m.preInitProc.s(cpsInit)) app(prc, m.initProc.s(cpsInit)) + app(prc, m.postInitProc.s(cpsInit)) app(prc, genSectionEnd(cpsInit)) app(prc, genSectionStart(cpsStmts)) app(prc, m.preInitProc.s(cpsStmts)) app(prc, m.initProc.s(cpsStmts)) + app(prc, m.postInitProc.s(cpsStmts)) app(prc, genSectionEnd(cpsStmts)) if optStackTrace in m.initProc.options and not m.PreventStackTrace: app(prc, deinitFrame(m.initProc)) @@ -1094,6 +1100,11 @@ proc newPreInitProc(m: BModule): BProc = # little hack so that unique temporaries are generated: result.labels = 100_000 +proc newPostInitProc(m: BModule): BProc = + result = newProc(nil, m) + # little hack so that unique temporaries are generated: + result.labels = 200_000 + proc rawNewModule(module: PSym, filename: string): BModule = new(result) InitLinkedList(result.headerFiles) @@ -1108,6 +1119,7 @@ proc rawNewModule(module: PSym, filename: string): BModule = result.initProc = newProc(nil, result) result.initProc.options = gOptions result.preInitProc = newPreInitProc(result) + result.postInitProc = newPostInitProc(result) initNodeTable(result.dataCache) result.typeStack = @[] result.forwardedProcs = @[] @@ -1128,6 +1140,7 @@ proc resetModule*(m: var BModule) = m.initProc = newProc(nil, m) m.initProc.options = gOptions m.preInitProc = newPreInitProc(m) + m.postInitProc = newPostInitProc(m) initNodeTable(m.dataCache) m.typeStack = @[] m.forwardedProcs = @[] diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index ad17194df..b5888d0f4 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -100,10 +100,8 @@ type headerFiles*: TLinkedList # needed headers to include typeInfoMarker*: TIntSet # needed for generating type information initProc*: BProc # code for init procedure + postInitProc*: BProc # code to be executed after the init proc preInitProc*: BProc # code executed before the init proc - # used for initialization code for - # .global. variables - # (or instantiated generic variables) typeStack*: TTypeSeq # used for type generation dataCache*: TNodeTable forwardedProcs*: TSymSeq # keep forwarded procs here diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 8d4946ab5..96eb3a5f4 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -219,8 +219,8 @@ proc getHiddenParam(routine: PSym): PSym = result = hidden.sym proc isInnerProc(s, outerProc: PSym): bool {.inline.} = - result = s.kind in {skProc, skMethod, skConverter} and - s.owner == outerProc and not isGenericRoutine(s) + result = s.kind in {skProc, skMethod, skConverter} and + s.skipGenericOwner == outerProc #s.typ.callConv == ccClosure proc addClosureParam(i: PInnerContext, e: PEnv) = @@ -288,6 +288,9 @@ proc interestingVar(s: PSym): bool {.inline.} = proc semCaptureSym*(s, owner: PSym) = if interestingVar(s) and owner.id != s.owner.id and s.kind != skResult: if owner.typ != nil and not isGenericRoutine(owner): + # XXX: is this really safe? + # if we capture a var from another generic routine, + # it won't be consider captured. owner.typ.callConv = ccClosure #echo "semCaptureSym ", owner.name.s, owner.id, " ", s.name.s, s.id # since the analysis is not entirely correct, we don't set 'tfCapturesEnv' diff --git a/compiler/parser.nim b/compiler/parser.nim index a92dde74f..699a50c62 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -627,6 +627,13 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = getTok(p) # we must consume a token here to prevend endless loops! result = ast.emptyNode +proc namedParams(p: var TParser, callee: PNode, + kind: TNodeKind, endTok: TTokType): PNode = + let a = callee + result = newNodeP(kind, p) + addSon(result, a) + exprColonEqExprListAux(p, endTok, result) + proc primarySuffix(p: var TParser, r: PNode): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? #| | doBlocks @@ -636,11 +643,8 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = result = r while p.tok.indent < 0: case p.tok.tokType - of tkParLe: - var a = result - result = newNodeP(nkCall, p) - addSon(result, a) - exprColonEqExprListAux(p, tkParRi, result) + of tkParLe: + result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result.sons[1].kind == nkExprColonExpr: result.kind = nkObjConstr else: @@ -653,10 +657,10 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = of tkDot: result = dotExpr(p, result) result = parseGStrLit(p, result) - of tkBracketLe: - result = indexExprList(p, result, nkBracketExpr, tkBracketRi) + of tkBracketLe: + result = namedParams(p, result, nkBracketExpr, tkBracketRi) of tkCurlyLe: - result = indexExprList(p, result, nkCurlyExpr, tkCurlyRi) + result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) else: break proc primary(p: var TParser, mode: TPrimaryMode): PNode diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 72d5a5fef..b9c32a680 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -207,6 +207,11 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = proc newTypeS(kind: TTypeKind, c: PContext): PType = result = newType(kind, getCurrOwner()) +proc newTypeWithSons*(c: PContext, kind: TTypeKind, + sons: seq[PType]): PType = + result = newType(kind, getCurrOwner()) + result.sons = sons + proc errorType*(c: PContext): PType = ## creates a type representing an error state result = newTypeS(tyError, c) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4591c3942..d88cc28b6 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -111,7 +111,11 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # var len = 0 # but won't be called # genericThatUsesLen(x) # marked as taking a closure? of skGenericParam: - if s.ast != nil: result = semExpr(c, s.ast) + if s.typ.kind == tyExpr: + result = newSymNode(s, n.info) + result.typ = s.typ.lastSon + elif s.ast != nil: + result = semExpr(c, s.ast) else: InternalError(n.info, "no default for") result = emptyNode @@ -888,10 +892,13 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = let tbody = ty.sons[0] for s in countup(0, tbody.len-2): let tParam = tbody.sons[s] - assert tParam.kind == tyGenericParam if tParam.sym.name == i: - let foundTyp = makeTypeDesc(c, ty.sons[s + 1]) - return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info) + let rawTyp = ty.sons[s + 1] + if rawTyp.kind == tyExpr: + return rawTyp.n + else: + let foundTyp = makeTypeDesc(c, rawTyp) + return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info) return else: # echo "TYPE FIELD ACCESS" @@ -1820,7 +1827,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkBind: Message(n.info, warnDeprecated, "bind") result = semExpr(c, n.sons[0], flags) - of nkTypeOfExpr: + of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy: var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) result = symNodeFromType(c, typ, n.info) of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 9dc99d173..601072833 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -38,7 +38,6 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, #t = instGenericContainer(c, a, t) t = generateTypeInstance(c, pt, a, t) #t = ReplaceTypeVarsT(cl, t) - t.flags.incl tfInstantiated s.typ = t addDecl(c, s) entry.concreteTypes[i] = t @@ -84,11 +83,20 @@ proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) = else: for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap) +proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) + proc instantiateBody(c: PContext, n: PNode, result: PSym) = if n.sons[bodyPos].kind != nkEmpty: + inc c.InGenericInst # add it here, so that recursive generic procs are possible: addDecl(c, result) pushProcCon(c, result) + # add params to scope + for i in 1 .. <result.typ.n.len: + var param = result.typ.n.sons[i].sym + param.owner = result + addParamOrResult(c, param, result.kind) + # debug result.typ.n maybeAddResult(c, result, n) var b = n.sons[bodyPos] var symMap: TIdTable @@ -100,6 +108,7 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) = #echo "code instantiated ", result.name.s excl(result.flags, sfForward) popProcCon(c) + dec c.InGenericInst proc fixupInstantiatedSymbols(c: PContext, s: PSym) = for i in countup(0, c.generics.len - 1): @@ -109,8 +118,6 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) = openScope(c) var n = oldPrc.ast n.sons[bodyPos] = copyTree(s.getBody) - if n.sons[paramsPos].kind != nkEmpty: - addParams(c, oldPrc.typ.n, oldPrc.kind) instantiateBody(c, n, oldPrc) closeScope(c) popInfoContext() @@ -123,7 +130,98 @@ proc sideEffectsCheck(c: PContext, s: PSym) = s.ast.sons[genericParamsPos].kind == nkEmpty: c.threadEntries.add(s) -proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, +proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType = + var cl: TReplTypeVars + InitIdTable(cl.symMap) + InitIdTable(cl.typeMap) + cl.info = info + cl.c = c + result = ReplaceTypeVarsT(cl, header) + +proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = + result = instGenericContainer(c, n.info, header) + +proc fixupProcType(c: PContext, genericType: PType, + inst: TInstantiation): PType = + # XXX: This is starting to look suspiciously like ReplaceTypeVarsT + # there are few apparent differences, but maybe the code could be + # moved over. + # * the code here uses the new genericSym.position property when + # doing lookups. + # * the handling of tyTypeDesc seems suspicious in ReplaceTypeVarsT + # typedesc params were previously handled in the second pass of + # semParamList + # * void (nkEmpty) params doesn't seem to be stripped in ReplaceTypeVarsT + result = genericType + if result == nil: return + + case genericType.kind + of tyGenericParam, tyTypeClass: + result = inst.concreteTypes[genericType.sym.position] + of tyTypeDesc: + result = inst.concreteTypes[genericType.sym.position] + if tfUnresolved in genericType.flags: + result = result.sons[0] + of tyExpr: + result = inst.concreteTypes[genericType.sym.position] + of tyOpenArray, tyArray, tySet, tySequence, tyTuple, tyProc, + tyPtr, tyVar, tyRef, tyOrdinal, tyRange, tyVarargs: + if genericType.sons == nil: return + var head = 0 + for i in 0 .. <genericType.sons.len: + let origType = genericType.sons[i] + var changed = fixupProcType(c, origType, inst) + if changed != genericType.sons[i]: + var changed = changed.skipIntLit + if result == genericType: + # the first detected change initializes the result + result = copyType(genericType, genericType.owner, false) + if genericType.n != nil: + result.n = copyTree(genericType.n) + + # XXX: doh, we have to treat seq and arrays as special case + # because sometimes the `@` magic will be applied to an empty + # sequence having the type tySequence(tyEmpty) + if changed.kind == tyEmpty and + genericType.kind notin {tyArray, tySequence}: + if genericType.kind == tyProc and i == 0: + # return types of procs are overwritten with nil + changed = nil + else: + # otherwise, `empty` is just erased from the signature + result.sons[i..i] = [] + if result.n != nil: result.n.sons[i..i] = [] + continue + + result.sons[head] = changed + + if result.n != nil: + if result.n.kind == nkRecList: + for son in result.n.sons: + if son.typ == origType: + son.typ = changed + son.sym = copySym(son.sym, true) + son.sym.typ = changed + if result.n.kind == nkFormalParams: + if i != 0: + let origParam = result.n.sons[head].sym + var param = copySym(origParam) + param.typ = changed + param.ast = origParam.ast + result.n.sons[head] = newSymNode(param) + + # won't be advanced on empty (void) nodes + inc head + + of tyGenericInvokation: + result = newTypeWithSons(c, tyGenericInvokation, genericType.sons) + for i in 1 .. <genericType.sons.len: + result.sons[i] = fixupProcType(c, result.sons[i], inst) + result = instGenericContainer(c, getInfoContext(-1), result) + + else: nil + +proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym = # no need to instantiate generic templates/macros: if fn.kind in {skTemplate, skMacro}: return fn @@ -137,11 +235,11 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, # we set the friend module: var oldFriend = c.friendModule c.friendModule = getModule(fn) + #let oldScope = c.currentScope + #c.currentScope = fn.scope result = copySym(fn, false) incl(result.flags, sfFromGeneric) - # keep the owner if it's an inner proc (for proper closure transformations): - if fn.owner.kind == skModule: - result.owner = getCurrOwner().owner + result.owner = fn result.ast = n pushOwner(result) openScope(c) @@ -152,16 +250,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, var entry = TInstantiation.new entry.sym = result instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[]) + result.typ = fixupProcType(c, fn.typ, entry[]) n.sons[genericParamsPos] = ast.emptyNode - # semantic checking for the parameters: - if n.sons[paramsPos].kind != nkEmpty: - removeDefaultParamValues(n.sons[ParamsPos]) - semParamList(c, n.sons[ParamsPos], nil, result) - else: - result.typ = newTypeS(tyProc, c) - rawAddSon(result.typ, nil) - result.typ.callConv = fn.typ.callConv - if result.kind == skIterator: result.typ.flags.incl(tfIterator) var oldPrc = GenericCacheGet(fn, entry[]) if oldPrc == nil: fn.procInstCache.safeAdd(entry) @@ -179,15 +269,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, popInfoContext() closeScope(c) # close scope for parameters popOwner() + #c.currentScope = oldScope c.friendModule = oldFriend dec(c.InstCounter) if result.kind == skMethod: finishMethod(c, result) - -proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = - var cl: TReplTypeVars - InitIdTable(cl.symMap) - InitIdTable(cl.typeMap) - cl.info = n.info - cl.c = c - result = ReplaceTypeVarsT(cl, header) + diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 4de1f9151..33c0adac1 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -721,7 +721,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # TGObj[T] = object # TAlias[T] = TGObj[T] # - a.sons[1] = semGenericParamList(c, a.sons[1], s.typ) + s.typ.n = semGenericParamList(c, a.sons[1], s.typ) + a.sons[1] = s.typ.n s.typ.size = -1 # could not be computed properly # we fill it out later. For magic generics like 'seq', it won't be filled # so we use tyEmpty instead of nil to not crash for strange conversions diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 4d465ead7..8ae23f851 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -226,7 +226,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = else: LocalError(n.info, errXExpectsOneTypeParam, "ordinal") result = newOrPrevType(tyError, prev, c) - + proc semTypeIdent(c: PContext, n: PNode): PSym = if n.kind == nkSym: result = n.sym @@ -236,16 +236,18 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = markUsed(n, result) if result.kind == skParam and result.typ.kind == tyTypeDesc: # This is a typedesc param. is it already bound? - # it's not bound when it's also used as return type for example - if result.typ.sonsLen > 0: + # it's not bound when it's used multiple times in the + # proc signature for example + if c.InGenericInst > 0: let bound = result.typ.sons[0].sym - if bound != nil: - return bound + if bound != nil: return bound return result if result.typ.sym == nil: LocalError(n.info, errTypeExpected) return errorSym(c, n) - return result.typ.sym + result = result.typ.sym.copySym + result.typ = copyType(result.typ, result.typ.owner, true) + result.typ.flags.incl tfUnresolved if result.kind != skType: # this implements the wanted ``var v: V, x: V`` feature ... var ov: TOverloadIter @@ -382,7 +384,7 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, # for ``{}`` we want to trigger the type mismatch in ``fitNode``: if r.kind != nkCurly or len(r) == 0: checkMinSonsLen(t, 1) - branch.sons[i] = fitNode(c, t.sons[0].typ, r) + branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r)) inc(covered) else: # constant sets have special rules @@ -585,81 +587,95 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = else: if sfGenSym notin param.flags: addDecl(c, param) -proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind): - tuple[typ: PType, id: PIdent] = - # if typ is not-nil, the param should be turned into a generic param - # if id is not nil, the generic param will bind just once (see below) +proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, + paramType: PType, paramName: string, + info: TLineInfo, anon = false): PType = + if procKind in {skMacro, skTemplate}: + # generic param types in macros and templates affect overload + # resolution, but don't work as generic params when it comes + # to proc instantiation. We don't need to lift such params here. + return + + proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = + let finalTypId = if typId != nil: typId + else: getIdent(paramName & ":type") + # is this a bindOnce type class already present in the param list? + for i in countup(0, genericParams.len - 1): + if genericParams.sons[i].sym.name.id == finalTypId.id: + return genericParams.sons[i].typ + + var s = newSym(skType, finalTypId, getCurrOwner(), info) + if typId == nil: s.flags.incl(sfAnon) + s.linkTo(typeClass) + s.position = genericParams.len + genericParams.addSon(newSymNode(s)) + result = typeClass + + # XXX: There are codegen errors if this is turned into a nested proc + template liftingWalk(typ: PType, anonFlag = false): expr = + liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag) + #proc liftingWalk(paramType: PType, anon = false): PType = + + var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name + else: nil + + template addImplicitGeneric(e: expr): expr = + addImplicitGenericImpl(e, paramTypId) + case paramType.kind: of tyExpr: if paramType.sonsLen == 0: # proc(a, b: expr) # no constraints, treat like generic param - result.typ = newTypeS(tyGenericParam, c) + result = addImplicitGeneric(newTypeS(tyGenericParam, c)) else: # proc(a: expr{string}, b: expr{nkLambda}) # overload on compile time values and AST trees - result.typ = newTypeS(tyExpr, c) - result.typ.sons = paramType.sons + result = addImplicitGeneric(c.newTypeWithSons(tyExpr, paramType.sons)) of tyTypeDesc: - if tfInstantiated notin paramType.flags: - result.typ = newTypeS(tyTypeDesc, c) - result.typ.sons = paramType.sons + if tfUnresolved notin paramType.flags: + result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons)) of tyDistinct: - result = paramTypeClass(c, paramType.lastSon, procKind) - # disable the bindOnce behavior for the type class - result.id = nil - return + if paramType.sonsLen == 1: + # disable the bindOnce behavior for the type class + result = liftingWalk(paramType.sons[0], true) + of tySequence, tySet, tyArray, tyOpenArray: + # XXX: this is a bit strange, but proc(s: seq) + # produces tySequence(tyGenericParam, null). + # This also seems to be true when creating aliases + # like: type myseq = distinct seq. + # Maybe there is another better place to associate + # the seq type class with the seq identifier. + if paramType.lastSon == nil: + let typ = c.newTypeWithSons(tyTypeClass, @[newTypeS(paramType.kind, c)]) + result = addImplicitGeneric(typ) + else: + for i in 0 .. <paramType.sons.len: + var lifted = liftingWalk(paramType.sons[i]) + if lifted != nil: + paramType.sons[i] = lifted + result = paramType of tyGenericBody: # type Foo[T] = object # proc x(a: Foo, b: Foo) - result.typ = newTypeS(tyTypeClass, c) - result.typ.addSonSkipIntLit(paramType) + var typ = newTypeS(tyTypeClass, c) + typ.addSonSkipIntLit(paramType) + result = addImplicitGeneric(typ) + of tyGenericInst: + for i in 1 .. (paramType.sons.len - 2): + var lifted = liftingWalk(paramType.sons[i]) + if lifted != nil: + paramType.sons[i] = lifted + result = paramType + + if result != nil: + result.kind = tyGenericInvokation + result.sons.setLen(result.sons.len - 1) of tyTypeClass: - result.typ = copyType(paramType, getCurrOwner(), false) + result = addImplicitGeneric(copyType(paramType, getCurrOwner(), false)) else: nil - # bindOnce by default - if paramType.sym != nil: result.id = paramType.sym.name - -proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, - paramType: PType, paramName: string, - info: TLineInfo): PType = - result = paramType - if procKind in {skMacro, skTemplate}: - # generic param types in macros and templates affect overload - # resolution, but don't work as generic params when it comes - # to proc instantiation. We don't need to lift such params here. - return - ## Params having implicit generic types or pseudo types such as 'expr' - ## need to be added to the generic params lists. - ## 'expr' is different from 'expr{string}' so we must first call - ## paramTypeClass to get the actual type we are going to use. - var (typeClass, paramTypId) = paramTypeClass(c, paramType, procKind) - let isAnon = paramTypId == nil - if typeClass != nil: - if isAnon: paramTypId = getIdent(paramName & ":type") - if genericParams == nil: - # genericParams is nil when the proc is being instantiated - # the resolved type will be in scope then - let s = searchInScopes(c, paramTypId) - # tests/run/tinterf triggers this: - if s != nil: result = s.typ - else: - LocalError(info, errCannotInstantiateX, paramName) - result = errorType(c) - else: - block addImplicitGeneric: - # is this a bindOnce type class already present in the param list? - for i in countup(0, genericParams.len - 1): - if genericParams.sons[i].sym.name.id == paramTypId.id: - result = genericParams.sons[i].typ - break addImplicitGeneric - var s = newSym(skType, paramTypId, getCurrOwner(), info) - if isAnon: s.flags.incl(sfAnon) - s.linkTo(typeClass) - s.position = genericParams.len - genericParams.addSon(newSymNode(s)) - result = typeClass + # result = liftingWalk(paramType) proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType = if n.kind == nkCurlyExpr: @@ -698,10 +714,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, length = sonsLen(a) hasType = a.sons[length-2].kind != nkEmpty hasDefault = a.sons[length-1].kind != nkEmpty - if hasType: typ = semParamType(c, a.sons[length-2], constraint) - + if hasDefault: def = semExprWithType(c, a.sons[length-1]) # check type compability between def.typ and typ: @@ -719,8 +734,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue for j in countup(0, length-3): var arg = newSymG(skParam, a.sons[j], c) - var finalType = liftParamType(c, kind, genericParams, typ, arg.name.s, - a.sons[length-2].info).skipIntLit + let lifted = liftParamType(c, kind, genericParams, typ, + arg.name.s, arg.info) + let finalType = if lifted != nil: lifted else: typ.skipIntLit arg.typ = finalType arg.position = counter arg.constraint = constraint @@ -738,11 +754,13 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # compiler only checks for 'nil': if skipTypes(r, {tyGenericInst}).kind != tyEmpty: if r.sym == nil or sfAnon notin r.sym.flags: - r = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info) + let lifted = liftParamType(c, kind, genericParams, r, "result", + n.sons[0].info) + if lifted != nil: r = lifted r.flags.incl tfRetType result.sons[0] = skipIntLit(r) res.typ = result.sons[0] - + proc semStmtListType(c: PContext, n: PNode, prev: PType): PType = checkMinSonsLen(n, 1) var length = sonsLen(n) @@ -788,29 +806,47 @@ proc semGenericParamInInvokation(c: PContext, n: PNode): PType = proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = result = newOrPrevType(tyGenericInvokation, prev, c) - var isConcrete = true + addSonSkipIntLit(result, s.typ) + + template addToResult(typ) = + if typ.isNil: + InternalAssert false + rawAddSon(result, typ) + else: addSonSkipIntLit(result, typ) + if s.typ == nil: LocalError(n.info, errCannotInstantiateX, s.name.s) return newOrPrevType(tyError, prev, c) - elif s.typ.kind != tyGenericBody: - isConcrete = false - elif sonsLen(n) != sonsLen(s.typ): - LocalError(n.info, errWrongNumberOfArguments) - return newOrPrevType(tyError, prev, c) - addSonSkipIntLit(result, s.typ) - # iterate over arguments: - for i in countup(1, sonsLen(n)-1): - var elem = semGenericParamInInvokation(c, n.sons[i]) - if containsGenericType(elem): isConcrete = false - #if elem.kind in {tyGenericParam, tyGenericInvokation}: isConcrete = false - if elem.isNil: rawAddSon(result, elem) - else: addSonSkipIntLit(result, elem) - if isConcrete: - if s.ast == nil: - LocalError(n.info, errCannotInstantiateX, s.name.s) - result = newOrPrevType(tyError, prev, c) - else: - result = instGenericContainer(c, n, result) + elif s.typ.kind == tyForward: + for i in countup(1, sonsLen(n)-1): + var elem = semGenericParamInInvokation(c, n.sons[i]) + addToResult(elem) + else: + internalAssert s.typ.kind == tyGenericBody + + var m = newCandidate(s, n) + matches(c, n, copyTree(n), m) + + if m.state != csMatch: + var err = "cannot instantiate " & typeToString(s.typ) & "\n" & + "got: (" & describeArgs(c, n) & ")\n" & + "but expected: (" & describeArgs(c, s.typ.n, 0) & ")" + LocalError(n.info, errGenerated, err) + return newOrPrevType(tyError, prev, c) + + var isConcrete = true + + for i in 1 .. <m.call.len: + let typ = m.call[i].typ.skipTypes({tyTypeDesc}) + if containsGenericType(typ): isConcrete = false + addToResult(typ) + + if isConcrete: + if s.ast == nil: + LocalError(n.info, errCannotInstantiateX, s.name.s) + result = newOrPrevType(tyError, prev, c) + else: + result = instGenericContainer(c, n, result) proc semTypeExpr(c: PContext, n: PNode): PType = var n = semExprWithType(c, n, {efDetermineType}) @@ -1014,55 +1050,61 @@ proc processMagicType(c: PContext, m: PSym) = of mPNimrodNode: nil else: LocalError(m.info, errTypeExpected) -proc semGenericConstraints(c: PContext, n: PNode, result: PType) = - var x = semTypeNode(c, n, nil) +proc semGenericConstraints(c: PContext, x: PType): PType = if x.kind in StructuralEquivTypes and ( sonsLen(x) == 0 or x.sons[0].kind in {tyGenericParam, tyEmpty}): - x = newConstraint(c, x.kind) - result.addSonSkipIntLit(x) + result = newConstraint(c, x.kind) + else: + result = newTypeWithSons(c, tyGenericParam, @[x]) proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = result = copyNode(n) if n.kind != nkGenericParams: illFormedAst(n) return - for i in countup(0, sonsLen(n)-1): + for i in countup(0, sonsLen(n)-1): var a = n.sons[i] if a.kind != nkIdentDefs: illFormedAst(n) - var L = sonsLen(a) - var def = a.sons[L-1] + let L = a.len + var def = a{-1} + let constraint = a{-2} var typ: PType - if a.sons[L-2].kind != nkEmpty: - typ = newTypeS(tyGenericParam, c) - semGenericConstraints(c, a.sons[L-2], typ) - if sonsLen(typ) == 1 and typ.sons[0].kind == tyTypeDesc: - typ = typ.sons[0] - elif def.kind != nkEmpty: typ = newTypeS(tyExpr, c) - else: typ = nil - for j in countup(0, L-3): - var s: PSym + + if constraint.kind != nkEmpty: + typ = semTypeNode(c, constraint, nil) + if typ.kind != tyExpr or typ.len == 0: + if typ.len == 0 and typ.kind == tyTypeDesc: + typ = newTypeS(tyGenericParam, c) + else: + typ = semGenericConstraints(c, typ) + + if def.kind != nkEmpty: + def = semConstExpr(c, def) if typ == nil: - s = newSymG(skType, a.sons[j], c) - s.typ = newTypeS(tyGenericParam, c) + if def.typ.kind != tyTypeDesc: + typ = newTypeWithSons(c, tyExpr, @[def.typ]) else: - case typ.kind - of tyTypeDesc: - s = newSymG(skType, a.sons[j], c) - s.typ = newTypeS(tyGenericParam, c) + if not containsGenericType(def.typ): + def = fitNode(c, typ, def) + + if typ == nil: + typ = newTypeS(tyGenericParam, c) + + for j in countup(0, L-3): + let finalType = if j == 0: typ + else: copyType(typ, typ.owner, false) + # it's important the we create an unique + # type for each generic param. the index + # of the parameter will be stored in the + # attached symbol. + var s = case finalType.kind of tyExpr: - #echo "GENERIC EXPR ", a.info.toFileLineCol - # not a type param, but an expression - # proc foo[x: expr](bar: int) what is this? - s = newSymG(skGenericParam, a.sons[j], c) - s.typ = typ + newSymG(skGenericParam, a.sons[j], c).linkTo(finalType) else: - # This handles cases like proc foo[t: tuple] - # XXX: we want to turn that into a type class - s = newSymG(skType, a.sons[j], c) - s.typ = typ + newSymG(skType, a.sons[j], c).linkTo(finalType) if def.kind != nkEmpty: s.ast = def - s.typ.sym = s if father != nil: addSonSkipIntLit(father, s.typ) - s.position = i + s.position = result.len addSon(result, newSymNode(s)) if sfGenSym notin s.flags: addDecl(c, s) + diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index a3e108a34..626d16d64 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -38,6 +38,7 @@ type proxyMatch*: bool # to prevent instantiations genericConverter*: bool # true if a generic converter needs to # be instantiated + typedescMatched: bool inheritancePenalty: int # to prefer closest father object type TTypeRelation* = enum # order is important! @@ -89,6 +90,9 @@ proc initCandidate*(c: var TCandidate, callee: PSym, binding: PNode, #debug(formalTypeParam) put(c.bindings, formalTypeParam, binding[i].typ) +proc newCandidate*(callee: PSym, binding: PNode, calleeScope = -1): TCandidate = + initCandidate(result, callee, binding, calleeScope) + proc copyCandidate(a: var TCandidate, b: TCandidate) = a.exactMatches = b.exactMatches a.subtypeMatches = b.subtypeMatches @@ -177,15 +181,9 @@ proc argTypeToString(arg: PNode): string = else: result = arg.typ.typeToString -proc NotFoundError*(c: PContext, n: PNode) = - # Gives a detailed error message; this is separated from semOverloadedCall, - # as semOverlodedCall is already pretty slow (and we need this information - # only in case of an error). - if c.InCompilesContext > 0: - # fail fast: - GlobalError(n.info, errTypeMismatch, "") - var result = msgKindToString(errTypeMismatch) - for i in countup(1, sonsLen(n) - 1): +proc describeArgs*(c: PContext, n: PNode, startIdx = 1): string = + result = "" + for i in countup(startIdx, n.len - 1): var arg = n.sons[i] if n.sons[i].kind == nkExprEqExpr: add(result, renderTree(n.sons[i].sons[0])) @@ -201,6 +199,16 @@ proc NotFoundError*(c: PContext, n: PNode) = if arg.typ.kind == tyError: return add(result, argTypeToString(arg)) if i != sonsLen(n) - 1: add(result, ", ") + +proc NotFoundError*(c: PContext, n: PNode) = + # Gives a detailed error message; this is separated from semOverloadedCall, + # as semOverlodedCall is already pretty slow (and we need this information + # only in case of an error). + if c.InCompilesContext > 0: + # fail fast: + GlobalError(n.info, errTypeMismatch, "") + var result = msgKindToString(errTypeMismatch) + add(result, describeArgs(c, n)) add(result, ')') var candidates = "" var o: TOverloadIter @@ -492,8 +500,11 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = of tyOrdinal: if isOrdinalType(a): var x = if a.kind == tyOrdinal: a.sons[0] else: a + result = typeRel(c, f.sons[0], x) if result < isGeneric: result = isNone + elif a.kind == tyGenericParam: + result = isGeneric of tyForward: InternalError("forward type in typeRel()") of tyNil: if a.kind == f.kind: result = isEqual @@ -611,7 +622,24 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = of tyGenericParam, tyTypeClass: var x = PType(idTableGet(c.bindings, f)) if x == nil: - result = matchTypeClass(c, f, a) + if c.calleeSym.kind == skType and f.kind == tyGenericParam and not c.typedescMatched: + # XXX: The fact that generic types currently use tyGenericParam for + # their parameters is really a misnomer. tyGenericParam means "match + # any value" and what we need is "match any type", which can be encoded + # by a tyTypeDesc params. Unfortunately, this requires more substantial + # changes in semtypinst and elsewhere. + if a.kind == tyTypeDesc: + if f.sons == nil or f.sons.len == 0: + result = isGeneric + else: + InternalAssert a.sons != nil and a.sons.len > 0 + c.typedescMatched = true + result = typeRel(c, f.sons[0], a.sons[0]) + else: + result = isNone + else: + result = matchTypeClass(c, f, a) + if result == isGeneric: var concrete = concreteType(c, a) if concrete == nil: @@ -909,16 +937,22 @@ proc matchesAux(c: PContext, n, nOrig: PNode, else: m.state = csNoMatch return - - var f = 1 # iterates over formal parameters - var a = 1 # iterates over the actual given arguments - m.state = csMatch # until proven otherwise + + var + # iterates over formal parameters + f = if m.callee.kind != tyGenericBody: 1 + else: 0 + # iterates over the actual given arguments + a = 1 + + m.state = csMatch # until proven otherwise m.call = newNodeI(n.kind, n.info) m.call.typ = base(m.callee) # may be nil - var formalLen = sonsLen(m.callee.n) + var formalLen = m.callee.n.len addSon(m.call, copyTree(n.sons[0])) var container: PNode = nil # constructed container var formal: PSym = nil + while a < n.len: if n.sons[a].kind == nkExprEqExpr: # named param diff --git a/compiler/types.nim b/compiler/types.nim index 6c4249b83..2564741d8 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -926,11 +926,21 @@ proc commonSuperclass*(a, b: PType): PType = if ancestors.contains(y.id): return y y = y.sons[0] -proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool -proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool = +type + TTypeAllowedFlag = enum + taField, + taHeap + + TTypeAllowedFlags = set[TTypeAllowedFlag] + +proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, + flags: TTypeAllowedFlags = {}): bool + +proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind, + flags: TTypeAllowedFlags = {}): bool = result = true if n != nil: - result = typeAllowedAux(marker, n.typ, kind) + result = typeAllowedAux(marker, n.typ, kind, flags) #if not result: debug(n.typ) if result: case n.kind @@ -938,7 +948,7 @@ proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool = nil else: for i in countup(0, sonsLen(n) - 1): - result = typeAllowedNode(marker, n.sons[i], kind) + result = typeAllowedNode(marker, n.sons[i], kind, flags) if not result: break proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]], @@ -959,8 +969,8 @@ proc skipGenericAlias*(t: PType): PType = proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool = for i in countup(0, typeClass.sonsLen - 1): let req = typeClass.sons[i] - var match = req.kind == skipTypes(t, {tyGenericInst, tyRange}).kind or - req.kind == skipTypes(t, {tyGenericInst}).kind + var match = req.kind == skipTypes(t, {tyRange, tyGenericInst}).kind + if not match: case req.kind of tyGenericBody: @@ -969,7 +979,9 @@ proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool = IdTablePut(bindings, typeClass, t) of tyTypeClass: match = matchTypeClass(bindings, req, t) - else: nil + elif t.kind == tyTypeClass: + match = matchTypeClass(bindings, t, req) + elif t.kind in {tyObject} and req.len != 0: # empty 'object' is fine as constraint in a type class match = sameType(t, req) @@ -982,13 +994,16 @@ proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool = # if the loop finished without returning, either all constraints matched # or none of them matched. result = if tfAny in typeClass.flags: false else: true + if result == true: + IdTablePut(bindings, typeClass, t) proc matchTypeClass*(typeClass, typ: PType): bool = var bindings: TIdTable initIdTable(bindings) result = matchTypeClass(bindings, typeClass, typ) -proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool = +proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, + flags: TTypeAllowedFlags = {}): bool = assert(kind in {skVar, skLet, skConst, skParam, skResult}) # if we have already checked the type, return true, because we stop the # evaluation if something is wrong: @@ -1002,66 +1017,70 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool = var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc}) case t2.kind of tyVar: - result = false # ``var var`` is always an invalid type: + result = taHeap in flags # ``var var`` is illegal on the heap: of tyOpenArray: - result = kind == skParam and typeAllowedAux(marker, t2, kind) + result = kind == skParam and typeAllowedAux(marker, t2, kind, flags) else: - result = kind in {skParam, skResult} and typeAllowedAux(marker, t2, kind) + result = kind in {skParam, skResult} and + typeAllowedAux(marker, t2, kind, flags) of tyProc: for i in countup(1, sonsLen(t) - 1): - result = typeAllowedAux(marker, t.sons[i], skParam) + result = typeAllowedAux(marker, t.sons[i], skParam, flags) if not result: break if result and t.sons[0] != nil: - result = typeAllowedAux(marker, t.sons[0], skResult) + result = typeAllowedAux(marker, t.sons[0], skResult, flags) of tyExpr, tyStmt, tyTypeDesc: result = true # XXX er ... no? these should not be allowed! + of tyEmpty: + result = taField in flags of tyTypeClass: result = true of tyGenericBody, tyGenericParam, tyForward, tyNone, tyGenericInvokation: result = false - of tyEmpty, tyNil: + of tyNil: result = kind == skConst of tyString, tyBool, tyChar, tyEnum, tyInt..tyBigNum, tyCString, tyPointer: result = true of tyOrdinal: result = kind == skParam of tyGenericInst, tyDistinct: - result = typeAllowedAux(marker, lastSon(t), kind) - of tyRange: + result = typeAllowedAux(marker, lastSon(t), kind, flags) + of tyRange: result = skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind in - {tyChar, tyEnum, tyInt..tyUInt64} + {tyChar, tyEnum, tyInt..tyFloat128} of tyOpenArray, tyVarargs: - result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar) + result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar, flags) of tySequence: result = t.sons[0].kind == tyEmpty or - typeAllowedAux(marker, t.sons[0], skVar) + typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap}) of tyArray: result = t.sons[1].kind == tyEmpty or - typeAllowedAux(marker, t.sons[1], skVar) + typeAllowedAux(marker, t.sons[1], skVar, flags) of tyRef: if kind == skConst: return false - result = typeAllowedAux(marker, t.sons[0], skVar) + result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap}) of tyPtr: - result = typeAllowedAux(marker, t.sons[0], skVar) - of tyArrayConstr, tyTuple, tySet, tyConst, tyMutable, tyIter: + result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap}) + of tyArrayConstr, tySet, tyConst, tyMutable, tyIter: for i in countup(0, sonsLen(t) - 1): - result = typeAllowedAux(marker, t.sons[i], kind) + result = typeAllowedAux(marker, t.sons[i], kind, flags) if not result: break - of tyObject: - if kind == skConst: return false + of tyObject, tyTuple: + if kind == skConst and t.kind == tyObject: return false + let flags = flags+{taField} for i in countup(0, sonsLen(t) - 1): - result = typeAllowedAux(marker, t.sons[i], kind) + result = typeAllowedAux(marker, t.sons[i], kind, flags) if not result: break - if result and t.n != nil: result = typeAllowedNode(marker, t.n, kind) + if result and t.n != nil: result = typeAllowedNode(marker, t.n, kind, flags) of tyProxy: # for now same as error node; we say it's a valid type as it should # prevent cascading errors: result = true - + proc typeAllowed(t: PType, kind: TSymKind): bool = var marker = InitIntSet() - result = typeAllowedAux(marker, t, kind) + result = typeAllowedAux(marker, t, kind, {}) proc align(address, alignment: biggestInt): biggestInt = result = (address + (alignment - 1)) and not (alignment - 1) |