better C++ support wrt 'this'

This commit is contained in:
Araq
2015-01-17 14:10:59 +01:00
parent 4a9647d4db
commit a2b7e6c392
2 changed files with 166 additions and 20 deletions

View File

@@ -217,7 +217,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
else:
genCallPattern()
proc genCppArg(p: BProc; ri: PNode; i: int; typ: PType): PRope =
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): PRope =
if i < sonsLen(typ):
# 'var T' is 'T&' in C++. This means we ignore the request of
# any nkHiddenAddr when it's a 'var T'.
@@ -225,20 +225,87 @@ proc genCppArg(p: BProc; ri: PNode; i: int; typ: PType): PRope =
if typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
result = genArgNoParam(p, ri.sons[i][0])
else:
result = genArg(p, ri.sons[i], typ.n.sons[i].sym)
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
else:
result = genArgNoParam(p, ri.sons[i])
discard """
Dot call syntax in C++
======================
so c2nim translates 'this' sometimes to 'T' and sometimes to 'var T'
both of which are wrong, but often more convenient to use.
For manual wrappers it can also be 'ptr T'
Fortunately we know which parameter is the 'this' parameter and so can fix this
mess in the codegen.
now ... if the *argument* is a 'ptr' the codegen shall emit -> and otherwise .
but this only depends on the argument and not on how the 'this' was declared
however how the 'this' was declared affects whether we end up with
wrong 'addr' and '[]' ops...
Since I'm tired I'll enumerate all the cases here:
var
x: ptr T
y: T
proc t(x: T)
x[].t() --> (*x).t() is correct.
y.t() --> y.t() is correct
proc u(x: ptr T)
x.u() --> needs to become x->u()
(addr y).u() --> needs to become y.u()
proc v(x: var T)
--> first skip the implicit 'nkAddr' node
x[].v() --> (*x).v() is correct, but might have been eliminated due
to the nkAddr node! So for this case we need to generate '->'
y.v() --> y.v() is correct
"""
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): PRope =
# for better or worse c2nim translates the 'this' argument to a 'var T'.
# However manual wrappers may also use 'ptr T'. In any case we support both
# for convenience.
internalAssert i < sonsLen(typ)
assert(typ.n.sons[i].kind == nkSym)
# if the parameter is lying (tyVar) and thus we required an additional deref,
# skip the deref:
if typ.sons[i].kind == tyVar:
let x = if ri[i].kind == nkHiddenAddr: ri[i][0] else: ri[i]
if x.kind in {nkHiddenDeref, nkDerefExpr}:
result = genArgNoParam(p, x[0])
result.app("->")
elif x.typ.kind in {tyVar, tyPtr}:
result = genArgNoParam(p, x)
result.app("->")
else:
result = genArgNoParam(p, x)
result.app(".")
elif typ.sons[i].kind == tyPtr:
if ri.sons[i].kind in {nkAddr, nkHiddenAddr}:
result = genArgNoParam(p, ri.sons[i][0])
result.app(".")
else:
result = genArgNoParam(p, ri.sons[i])
result.app("->")
else:
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
result.app(".")
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): PRope =
var i = 0
var j = 1
while i < pat.len:
case pat[i]
of '@':
result.app genCppArg(p, ri, j, typ)
result.app genOtherArg(p, ri, j, typ)
for k in j+1 .. < ri.len:
result.app(~", ")
result.app genCppArg(p, ri, k, typ)
result.app genOtherArg(p, ri, k, typ)
inc i
of '#':
if pat[i+1] in {'+', '@'}:
@@ -247,22 +314,21 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): PRope =
let typ = skipTypes(ri.sons[0].typ, abstractInst)
if pat[i+1] == '+': result.app genArgNoParam(p, ri.sons[0])
result.app(~"(")
result.app genCppArg(p, ri, 1, typ)
result.app genOtherArg(p, ri, 1, typ)
for k in j+1 .. < ri.len:
result.app(~", ")
result.app genCppArg(p, ri, k, typ)
result.app genOtherArg(p, ri, k, typ)
result.app(~")")
else:
localError(ri.info, "call expression expected for C++ pattern")
inc i
else:
result.app genCppArg(p, ri, j, typ)
if pat[i+1] == '.':
let param = typ.n.sons[j].sym
if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: result.app(~"->")
else: result.app(~".")
elif pat[i+1] == '.':
result.app genThisArg(p, ri, j, typ)
inc i
else:
result.app genOtherArg(p, ri, j, typ)
inc j
inc i
of '\'':
inc i
let stars = i
@@ -271,12 +337,18 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): PRope =
let j = pat[i].ord - '0'.ord
var t = typ.sons[j]
for k in 1..i-stars:
if t != nil and t.len > 0: t = t.elemType
if t != nil and t.len > 0:
t = if t.kind == tyGenericInst: t.sons[1] else: t.elemType
if t == nil: result.app(~"void")
else: result.app(getTypeDesc(p.module, t))
inc i
else:
result.app(toRope($pat[i]))
inc i
let start = i
while i < pat.len:
if pat[i] notin {'@', '#', '\''}: inc(i)
else: break
if i - 1 >= start:
app(result, substr(pat, start, i - 1))
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op, a: TLoc
@@ -305,16 +377,14 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
line(p, cpsStmts, pl)
else:
var pl: PRope = nil
var param = typ.n.sons[1].sym
app(pl, genCppArg(p, ri, 1, typ))
if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, ~"->")
else: app(pl, ~".")
#var param = typ.n.sons[1].sym
app(pl, genThisArg(p, ri, 1, typ))
app(pl, op.r)
var params: PRope
for i in countup(2, length - 1):
if params != nil: params.app(~", ")
assert(sonsLen(typ) == sonsLen(typ.n))
app(params, genCppArg(p, ri, i, typ))
app(params, genOtherArg(p, ri, i, typ))
fixupCall(p, le, ri, d, pl, params)
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =

76
tests/iter/tscheduler.nim Normal file
View File

@@ -0,0 +1,76 @@
discard """
output: '''a1 5
a2 10
a1 3
a1 1
a2 8
a2 6
a2 4
a2 2'''
"""
import os, strutils, times, algorithm
type TaskFn = iterator (): float
type Task = object
coro: TaskFn
next_run: float
type Scheduler = object
tasks: seq[Task]
proc newScheduler(): Scheduler =
var s = Scheduler()
s.tasks = @[]
return s
proc start(this: var Scheduler, task: TaskFn) =
var t = Task()
t.coro = task
t.next_run = 0.0
this.tasks.add(t)
proc run(this: var Scheduler) =
while this.tasks.len > 0:
var dead: seq[int] = @[]
for i in this.tasks.low..this.tasks.high:
var task = this.tasks[i]
if finished(task.coro):
dead.add(i)
continue
if task.next_run <= epochTime():
task.next_run = task.coro() + epochTime()
this.tasks[i] = task
for i in dead:
this.tasks.delete(i)
if this.tasks.len > 0:
sort(this.tasks, proc (t1: Task, t2: Task): int = cmp(t1.next_run, t2.next_run))
sleep(int((this.tasks[0].next_run - epochTime()) * 1000))
iterator a1(): float {.closure.} =
var k = 5
while k > 0:
echo "a1 $1" % [$k]
dec k, 2
yield 0.5
iterator a2(): float {.closure.} =
var k = 10
while k > 0:
echo "a2 $1" % [$k]
dec k, 2
yield 1.5
var sched = newScheduler()
sched.start(a1)
sched.start(a2)
sched.run()