diff options
Diffstat (limited to 'compiler/cgmeth.nim')
-rw-r--r-- | compiler/cgmeth.nim | 277 |
1 files changed, 146 insertions, 131 deletions
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 6f7d9f489..ca97d0494 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -7,13 +7,20 @@ # distribution, for details about the copyright. # -## This module implements code generation for multi methods. +## This module implements code generation for methods. import - intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys, - sempass2, strutils, modulegraphs + options, ast, msgs, idents, renderer, types, magicsys, + sempass2, modulegraphs, lineinfos, astalgo -proc genConv(n: PNode, d: PType, downcast: bool): PNode = +import std/intsets + +when defined(nimPreviewSlimSystem): + import std/assertions + +import std/[tables] + +proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode = var dest = skipTypes(d, abstractPtrs) var source = skipTypes(n.typ, abstractPtrs) if (source.kind == tyObject) and (dest.kind == tyObject): @@ -23,13 +30,13 @@ proc genConv(n: PNode, d: PType, downcast: bool): PNode = result = n elif diff < 0: result = newNodeIT(nkObjUpConv, n.info, d) - addSon(result, n) - if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed") + result.add n + if downcast: internalError(conf, n.info, "cgmeth.genConv: no upcast allowed") elif diff > 0: result = newNodeIT(nkObjDownConv, n.info, d) - addSon(result, n) + result.add n if not downcast: - internalError(n.info, "cgmeth.genConv: no downcast allowed") + internalError(conf, n.info, "cgmeth.genConv: no downcast allowed") else: result = n else: @@ -37,53 +44,58 @@ proc genConv(n: PNode, d: PType, downcast: bool): PNode = proc getDispatcher*(s: PSym): PSym = ## can return nil if is has no dispatcher. - let dispn = lastSon(s.ast) - if dispn.kind == nkSym: - let disp = dispn.sym - if sfDispatcher in disp.flags: result = disp + if dispatcherPos < s.ast.len: + result = s.ast[dispatcherPos].sym + doAssert sfDispatcher in result.flags + else: + result = nil -proc methodCall*(n: PNode): PNode = +proc methodCall*(n: PNode; conf: ConfigRef): PNode = result = n # replace ordinary method by dispatcher method: - let disp = getDispatcher(result.sons[0].sym) + let disp = getDispatcher(result[0].sym) if disp != nil: - result.sons[0].sym = disp + result[0].typ = disp.typ + result[0].sym = disp # change the arguments to up/downcasts to fit the dispatcher's parameters: - for i in countup(1, sonsLen(result)-1): - result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true) + for i in 1..<result.len: + result[i] = genConv(result[i], disp.typ[i], true, conf) else: - localError(n.info, "'" & $result.sons[0] & "' lacks a dispatcher") + localError(conf, n.info, "'" & $result[0] & "' lacks a dispatcher") type MethodResult = enum No, Invalid, Yes -proc sameMethodBucket(a, b: PSym): MethodResult = +proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult = + result = No if a.name.id != b.name.id: return - if sonsLen(a.typ) != sonsLen(b.typ): + if a.typ.signatureLen != b.typ.signatureLen: return - for i in countup(1, sonsLen(a.typ) - 1): - var aa = a.typ.sons[i] - var bb = b.typ.sons[i] + var i = 0 + for x, y in paramTypePairs(a.typ, b.typ): + inc i + var aa = x + var bb = y while true: aa = skipTypes(aa, {tyGenericInst, tyAlias}) bb = skipTypes(bb, {tyGenericInst, tyAlias}) - if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef}: - aa = aa.lastSon - bb = bb.lastSon + if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}: + aa = aa.elementType + bb = bb.elementType else: break - if sameType(aa, bb): + if sameType(x, y): if aa.kind == tyObject and result != Invalid: result = Yes - elif aa.kind == tyObject and bb.kind == tyObject: + elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods): let diff = inheritanceDiff(bb, aa) if diff < 0: if result != Invalid: result = Yes else: return No - elif diff != high(int): + elif diff != high(int) and sfFromGeneric notin (a.flags+b.flags): result = Invalid else: return No @@ -91,131 +103,131 @@ proc sameMethodBucket(a, b: PSym): MethodResult = return No if result == Yes: # check for return type: - if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): - if b.typ.sons[0] != nil and b.typ.sons[0].kind == tyExpr: + # ignore flags of return types; # bug #22673 + if not sameTypeOrNil(a.typ.returnType, b.typ.returnType, {IgnoreFlags}): + if b.typ.returnType != nil and b.typ.returnType.kind == tyUntyped: # infer 'auto' from the base to make it consistent: - b.typ.sons[0] = a.typ.sons[0] + b.typ.setReturnType a.typ.returnType else: return No proc attachDispatcher(s: PSym, dispatcher: PNode) = - var L = s.ast.len-1 - var x = s.ast.sons[L] - if x.kind == nkSym and sfDispatcher in x.sym.flags: + if dispatcherPos < s.ast.len: # we've added a dispatcher already, so overwrite it - s.ast.sons[L] = dispatcher + s.ast[dispatcherPos] = dispatcher else: - s.ast.add(dispatcher) + setLen(s.ast.sons, dispatcherPos+1) + if s.ast[resultPos] == nil: + s.ast[resultPos] = newNodeI(nkEmpty, s.info) + s.ast[dispatcherPos] = dispatcher -proc createDispatcher(s: PSym): PSym = - var disp = copySym(s) +proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym = + var disp = copySym(s, idgen) incl(disp.flags, sfDispatcher) excl(disp.flags, sfExported) - disp.typ = copyType(disp.typ, disp.typ.owner, false) + let old = disp.typ + disp.typ = copyType(disp.typ, idgen, disp.typ.owner) + copyTypeProps(g, idgen.module, disp.typ, old) + # we can't inline the dispatcher itself (for now): - if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault + if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall disp.ast = copyTree(s.ast) - disp.ast.sons[bodyPos] = ast.emptyNode - disp.loc.r = nil - if s.typ.sons[0] != nil: - if disp.ast.sonsLen > resultPos: - disp.ast.sons[resultPos].sym = copySym(s.ast.sons[resultPos].sym) + disp.ast[bodyPos] = newNodeI(nkEmpty, s.info) + disp.loc.snippet = "" + if s.typ.returnType != nil: + if disp.ast.len > resultPos: + disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen) else: # We've encountered a method prototype without a filled-in # resultPos slot. We put a placeholder in there that will # be updated in fixupDispatcher(). - disp.ast.addSon(ast.emptyNode) + disp.ast.add newNodeI(nkEmpty, s.info) attachDispatcher(s, newSymNode(disp)) # attach to itself to prevent bugs: attachDispatcher(disp, newSymNode(disp)) return disp -proc fixupDispatcher(meth, disp: PSym) = +proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) = # We may have constructed the dispatcher from a method prototype # and need to augment the incomplete dispatcher with information # from later definitions, particularly the resultPos slot. Also, # the lock level of the dispatcher needs to be updated/checked # against that of the method. - if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and - disp.ast.sons[resultPos] == ast.emptyNode: - disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos]) + if disp.ast.len > resultPos and meth.ast.len > resultPos and + disp.ast[resultPos].kind == nkEmpty: + disp.ast[resultPos] = copyTree(meth.ast[resultPos]) - # The following code works only with lock levels, so we disable - # it when they're not available. - when declared(TLockLevel): - proc `<`(a, b: TLockLevel): bool {.borrow.} - proc `==`(a, b: TLockLevel): bool {.borrow.} - if disp.typ.lockLevel == UnspecifiedLockLevel: - disp.typ.lockLevel = meth.typ.lockLevel - elif meth.typ.lockLevel != UnspecifiedLockLevel and - meth.typ.lockLevel != disp.typ.lockLevel: - message(meth.info, warnLockLevel, - "method has lock level $1, but another method has $2" % - [$meth.typ.lockLevel, $disp.typ.lockLevel]) - # XXX The following code silences a duplicate warning in - # checkMethodeffects() in sempass2.nim for now. - if disp.typ.lockLevel < meth.typ.lockLevel: - disp.typ.lockLevel = meth.typ.lockLevel +proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) = + var witness: PSym = nil + if s.typ.firstParamType.owner.getModule != s.getModule and vtables in g.config.features and not + g.config.isDefined("nimInternalNonVtablesTesting"): + localError(g.config, s.info, errGenerated, "method `" & s.name.s & + "` can be defined only in the same module with its type (" & s.typ.firstParamType.typeToString() & ")") + if sfImportc in s.flags: + localError(g.config, s.info, errGenerated, "method `" & s.name.s & + "` is not allowed to have 'importc' pragmas") -proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) = - let L = len(g.methods) - var witness: PSym - for i in countup(0, L - 1): + for i in 0..<g.methods.len: let disp = g.methods[i].dispatcher - case sameMethodBucket(disp, s) + case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions) of Yes: - add(g.methods[i].methods, s) - attachDispatcher(s, lastSon(disp.ast)) - fixupDispatcher(s, disp) + g.methods[i].methods.add(s) + attachDispatcher(s, disp.ast[dispatcherPos]) + fixupDispatcher(s, disp, g.config) #echo "fixup ", disp.name.s, " ", disp.id - when useEffectSystem: checkMethodEffects(disp, s) + when useEffectSystem: checkMethodEffects(g, disp, s) if {sfBase, sfFromGeneric} * s.flags == {sfBase} and g.methods[i].methods[0] != s: # already exists due to forwarding definition? - localError(s.info, "method is not a base") + localError(g.config, s.info, "method is not a base") return of No: discard of Invalid: if witness.isNil: witness = g.methods[i].methods[0] # create a new dispatcher: - add(g.methods, (methods: @[s], dispatcher: createDispatcher(s))) + # stores the id and the position + if s.typ.firstParamType.skipTypes(skipPtrs).itemId notin g.bucketTable: + g.bucketTable[s.typ.firstParamType.skipTypes(skipPtrs).itemId] = 1 + else: + g.bucketTable.inc(s.typ.firstParamType.skipTypes(skipPtrs).itemId) + g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen))) #echo "adding ", s.info - #if fromCache: - # internalError(s.info, "no method dispatcher found") if witness != nil: - localError(s.info, "invalid declaration order; cannot attach '" & s.name.s & - "' to method defined here: " & $witness.info) + localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s & + "' to method defined here: " & g.config$witness.info) elif sfBase notin s.flags: - message(s.info, warnUseBase) + message(g.config, s.info, warnUseBase) -proc relevantCol(methods: TSymSeq, col: int): bool = +proc relevantCol*(methods: seq[PSym], col: int): bool = # returns true iff the position is relevant - var t = methods[0].typ.sons[col].skipTypes(skipPtrs) + result = false + var t = methods[0].typ[col].skipTypes(skipPtrs) if t.kind == tyObject: - for i in countup(1, high(methods)): - let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs) + for i in 1..high(methods): + let t2 = skipTypes(methods[i].typ[col], skipPtrs) if not sameType(t2, t): return true proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int = - for col in countup(1, sonsLen(a.typ) - 1): + result = 0 + for col in FirstParamAt..<a.typ.signatureLen: if contains(relevantCols, col): - var aa = skipTypes(a.typ.sons[col], skipPtrs) - var bb = skipTypes(b.typ.sons[col], skipPtrs) + var aa = skipTypes(a.typ[col], skipPtrs) + var bb = skipTypes(b.typ[col], skipPtrs) var d = inheritanceDiff(aa, bb) if (d != high(int)) and d != 0: return d -proc sortBucket(a: var TSymSeq, relevantCols: IntSet) = +proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) = # we use shellsort here; fast and simple - var n = len(a) + var n = a.len var h = 1 while true: h = 3 * h + 1 if h > n: break while true: h = h div 3 - for i in countup(h, n - 1): + for i in h..<n: var v = a[i] var j = i while cmpSignatures(a[j - h], v, relevantCols) >= 0: @@ -225,69 +237,72 @@ proc sortBucket(a: var TSymSeq, relevantCols: IntSet) = a[j] = v if h == 1: break -proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = - var base = lastSon(methods[0].ast).sym +proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym = + var base = methods[0].ast[dispatcherPos].sym result = base - var paramLen = sonsLen(base.typ) + var paramLen = base.typ.signatureLen var nilchecks = newNodeI(nkStmtList, base.info) var disp = newNodeI(nkIfStmt, base.info) - var ands = getSysSym("and") - var iss = getSysSym("of") - for col in countup(1, paramLen - 1): + var ands = getSysMagic(g, unknownLineInfo, "and", mAnd) + var iss = getSysMagic(g, unknownLineInfo, "of", mOf) + let boolType = getSysType(g, unknownLineInfo, tyBool) + for col in FirstParamAt..<paramLen: if contains(relevantCols, col): - let param = base.typ.n.sons[col].sym + let param = base.typ.n[col].sym if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: - addSon(nilchecks, newTree(nkCall, - newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param))) - for meth in countup(0, high(methods)): + nilchecks.add newTree(nkCall, + newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(param)) + for meth in 0..high(methods): var curr = methods[meth] # generate condition: var cond: PNode = nil - for col in countup(1, paramLen - 1): + for col in FirstParamAt..<paramLen: if contains(relevantCols, col): - var isn = newNodeIT(nkCall, base.info, getSysType(tyBool)) - addSon(isn, newSymNode(iss)) - let param = base.typ.n.sons[col].sym - addSon(isn, newSymNode(param)) - addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col])) + var isn = newNodeIT(nkCall, base.info, boolType) + isn.add newSymNode(iss) + let param = base.typ.n[col].sym + isn.add newSymNode(param) + isn.add newNodeIT(nkType, base.info, curr.typ[col]) if cond != nil: - var a = newNodeIT(nkCall, base.info, getSysType(tyBool)) - addSon(a, newSymNode(ands)) - addSon(a, cond) - addSon(a, isn) + var a = newNodeIT(nkCall, base.info, boolType) + a.add newSymNode(ands) + a.add cond + a.add isn cond = a else: cond = isn - let retTyp = base.typ.sons[0] + let retTyp = base.typ.returnType let call = newNodeIT(nkCall, base.info, retTyp) - addSon(call, newSymNode(curr)) - for col in countup(1, paramLen - 1): - addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym), - curr.typ.sons[col], false)) + call.add newSymNode(curr) + for col in 1..<paramLen: + call.add genConv(newSymNode(base.typ.n[col].sym), + curr.typ[col], false, g.config) var ret: PNode if retTyp != nil: var a = newNodeI(nkFastAsgn, base.info) - addSon(a, newSymNode(base.ast.sons[resultPos].sym)) - addSon(a, call) + a.add newSymNode(base.ast[resultPos].sym) + a.add call ret = newNodeI(nkReturnStmt, base.info) - addSon(ret, a) + ret.add a else: ret = call if cond != nil: var a = newNodeI(nkElifBranch, base.info) - addSon(a, cond) - addSon(a, ret) - addSon(disp, a) + a.add cond + a.add ret + disp.add a else: disp = ret nilchecks.add disp - result.ast.sons[bodyPos] = nilchecks + nilchecks.flags.incl nfTransf # should not be further transformed + result.ast[bodyPos] = nilchecks -proc generateMethodDispatchers*(g: ModuleGraph): PNode = - result = newNode(nkStmtList) - for bucket in countup(0, len(g.methods) - 1): +proc generateIfMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator) = + for bucket in 0..<g.methods.len: var relevantCols = initIntSet() - for col in countup(1, sonsLen(g.methods[bucket].methods[0].typ) - 1): + for col in FirstParamAt..<g.methods[bucket].methods[0].typ.signatureLen: if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col) + if optMultiMethods notin g.config.globalOptions: + # if multi-methods are not enabled, we are interested only in the first field + break sortBucket(g.methods[bucket].methods, relevantCols) - addSon(result, - newSymNode(genDispatcher(g.methods[bucket].methods, relevantCols))) + g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, idgen) |