mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 01:14:41 +00:00
bugfix: wrong assertions for C++ code generation; some solaris support; first steps to an effect system
This commit is contained in:
@@ -277,6 +277,13 @@ const
|
||||
# getting ready for the future expr/stmt merge
|
||||
nkWhen* = nkWhenStmt
|
||||
nkWhenExpr* = nkWhenStmt
|
||||
nkEffectList* = nkArgList
|
||||
# hacks ahead: an nkEffectList is a node with 4 children:
|
||||
exceptionEffects* = 0 # exceptions at position 0
|
||||
readEffects* = 1 # read effects at position 1
|
||||
writeEffects* = 2 # write effects at position 2
|
||||
tagEffects* = 3 # user defined tag ('gc', 'time' etc.)
|
||||
effectListLen* = 4 # list of effects list
|
||||
|
||||
type
|
||||
TTypeKind* = enum # order is important!
|
||||
|
||||
@@ -1131,11 +1131,6 @@ proc writeModule(m: BModule, pending: bool) =
|
||||
addFileToCompile(cfilenoext)
|
||||
addFileToLink(cfilenoext)
|
||||
|
||||
proc genPlatformAsserts(m: BModule) =
|
||||
appf(m.s[cfsForwardTypes],
|
||||
"typedef assert_numbits[sizeof(NI) == sizeof(void*) &&" &
|
||||
"NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];$N")
|
||||
|
||||
proc myClose(b: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
if b == nil or passes.skipCodegen(n): return
|
||||
@@ -1149,7 +1144,6 @@ proc myClose(b: PPassContext, n: PNode): PNode =
|
||||
if sfMainModule in m.module.flags:
|
||||
var disp = generateMethodDispatchers()
|
||||
for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
|
||||
genPlatformAsserts(m)
|
||||
genMainProc(m)
|
||||
# we need to process the transitive closure because recursive module
|
||||
# deps are allowed (and the system module is processed in the wrong
|
||||
|
||||
@@ -41,7 +41,7 @@ const
|
||||
wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop,
|
||||
wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
|
||||
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
||||
wLinearScanEnd, wPatterns}
|
||||
wLinearScanEnd, wPatterns, wEffects}
|
||||
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
||||
wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
|
||||
wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame}
|
||||
@@ -292,14 +292,14 @@ proc processOption(c: PContext, n: PNode) =
|
||||
if n.sons[1].kind != nkIdent:
|
||||
invalidPragma(n)
|
||||
else:
|
||||
case whichKeyword(n.sons[1].ident)
|
||||
of wSpeed:
|
||||
case n.sons[1].ident.s.normalize
|
||||
of "speed":
|
||||
incl(gOptions, optOptimizeSpeed)
|
||||
excl(gOptions, optOptimizeSize)
|
||||
of wSize:
|
||||
of "size":
|
||||
excl(gOptions, optOptimizeSpeed)
|
||||
incl(gOptions, optOptimizeSize)
|
||||
of wNone:
|
||||
of "none":
|
||||
excl(gOptions, optOptimizeSpeed)
|
||||
excl(gOptions, optOptimizeSize)
|
||||
else: LocalError(n.info, errNoneSpeedOrSizeExpected)
|
||||
@@ -656,6 +656,9 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
||||
of wEmit: PragmaEmit(c, it)
|
||||
of wUnroll: PragmaUnroll(c, it)
|
||||
of wLinearScanEnd: PragmaLinearScanEnd(c, it)
|
||||
of wEffects:
|
||||
# is later processed in effect analysis:
|
||||
noVal(it)
|
||||
of wIncompleteStruct:
|
||||
noVal(it)
|
||||
if sym.typ == nil: invalidPragma(it)
|
||||
|
||||
@@ -14,7 +14,7 @@ import
|
||||
# way had some inherent problems. Performs:
|
||||
#
|
||||
# * procvar checks
|
||||
# * effect tracking
|
||||
# * effect+exception tracking
|
||||
# * closure analysis
|
||||
# * checks for invalid usages of compiletime magics (not implemented)
|
||||
# * checks for invalid usages of PNimNode (not implemented)
|
||||
@@ -29,6 +29,21 @@ import
|
||||
# --> a TR macro can annotate the proc with user defined annotations
|
||||
# --> the effect system can access these
|
||||
|
||||
# Load&Store analysis is performed on *paths*. A path is an access like
|
||||
# obj.x.y[i].z; splitting paths up causes some problems:
|
||||
#
|
||||
# var x = obj.x
|
||||
# var z = x.y[i].z
|
||||
#
|
||||
# Alias analysis is affected by this too! A good solution is *type splitting*:
|
||||
# T becomes T1 and T2 if it's known that T1 and T2 can't alias.
|
||||
#
|
||||
# An aliasing problem and a race condition are effectively the same problem.
|
||||
# Type based alias analysis is nice but not sufficient; especially splitting
|
||||
# an array and filling it in parallel should be supported but is not easily
|
||||
# done: It essentially requires a built-in 'indexSplit' operation and dependent
|
||||
# typing.
|
||||
|
||||
proc sem2call(c: PContext, n: PNode): PNode =
|
||||
assert n.kind in nkCallKinds
|
||||
|
||||
@@ -37,4 +52,145 @@ proc sem2call(c: PContext, n: PNode): PNode =
|
||||
proc sem2sym(c: PContext, n: PNode): PNode =
|
||||
assert n.kind == nkSym
|
||||
|
||||
|
||||
# ------------------------ exception tracking -------------------------------
|
||||
|
||||
discard """
|
||||
exception tracking:
|
||||
|
||||
a() # raises 'x', 'e'
|
||||
try:
|
||||
b() # raises 'e'
|
||||
except e:
|
||||
# must not undo 'e' here; hrm
|
||||
c()
|
||||
|
||||
--> we need a stack of scopes for this analysis
|
||||
|
||||
|
||||
Effect tracking:
|
||||
|
||||
We track the effects per proc; forward declarations and indirect calls cause
|
||||
problems: Forward declarations are computed lazily (we do this pass after
|
||||
a whole module) and indirect calls are assumed the worst, unless they have
|
||||
an effect annotation.
|
||||
"""
|
||||
|
||||
type
|
||||
TEffects = object
|
||||
exc: PNode # stack of exceptions
|
||||
bottom: int
|
||||
|
||||
PEffects = var TEffects
|
||||
|
||||
proc throws(tracked: PEffects, n: PNode) =
|
||||
# since a 'raise' statement occurs rarely and we need distinct reasons;
|
||||
# we simply do not merge anything here, this would be problematic for the
|
||||
# stack of exceptions anyway:
|
||||
tracked.exc.add n
|
||||
|
||||
proc excType(n: PNode): PType =
|
||||
assert n.kind == nkRaiseStmt
|
||||
# reraise is like raising E_Base:
|
||||
let t = if n.sons[0].kind == nkEmpty: sysTypeFromName"E_Base"
|
||||
else: n.sons[0].typ
|
||||
result = skipTypes(t, skipPtrs)
|
||||
|
||||
proc mergeEffects(a: PEffects, b: PNode) =
|
||||
var aa = a.exc
|
||||
for effect in items(b):
|
||||
block search
|
||||
for i in a.bottom .. <aa.len:
|
||||
if sameType(aa[i].excType, b.excType): break search
|
||||
throws(a, effect)
|
||||
|
||||
proc listEffects(a: PEffects) =
|
||||
var aa = a.exc
|
||||
for e in items(aa):
|
||||
Message(e.info, hintUser, renderTree(e))
|
||||
|
||||
proc catches(tracked: PEffects, e: PType) =
|
||||
let e = skipTypes(e, skipPtrs)
|
||||
let L = tracked.exc.len
|
||||
var i = tracked.bottom
|
||||
while i < L:
|
||||
# e supertype of r?
|
||||
if inheritanceDiff(e, tracked.exc[i].excType) <= 0:
|
||||
tracked.exc.sons[i] = tracked.exc.sons[L-1]
|
||||
dec L
|
||||
else:
|
||||
inc i
|
||||
|
||||
proc catchesAll(tracked: PEffects) =
|
||||
setLen(tracked.exc.sons, tracked.bottom)
|
||||
|
||||
proc track(tracked: PEffects, n: PNode)
|
||||
proc trackTryStmt(tracked: PEffects, n: PNode) =
|
||||
let oldBottom = tracked.bottom
|
||||
tracked.bottom = tracked.exc.len
|
||||
track(tracked, n.sons[0])
|
||||
for i in 1 .. < n.len:
|
||||
let b = n.sons[i]
|
||||
let blen = sonsLen(b)
|
||||
if b.kind == nkExceptBranch:
|
||||
if blen == 1:
|
||||
catchesAll(tracked)
|
||||
else:
|
||||
for j in countup(0, blen - 2):
|
||||
assert(b.sons[j].kind == nkType)
|
||||
catches(tracked, b.sons[j].typ)
|
||||
else:
|
||||
assert b.kind == nkFinally
|
||||
track(tracked, b.sons[blen-1])
|
||||
tracked.bottom = oldBottom
|
||||
|
||||
proc isIndirectCall(n: PNode): bool =
|
||||
result = n.kind != nkSym or n.sym.kind notin routineKinds
|
||||
|
||||
proc isForwardedProc(n: PNode): bool =
|
||||
result = n.kind == nkSym and sfForward in n.sym.flags
|
||||
|
||||
proc trackPragmaStmt(tracked: PEffects, n: PNode) =
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
if whichPragma(it) == wEffects:
|
||||
# list the computed effects up to here:
|
||||
listEffects(tracked)
|
||||
|
||||
proc track(tracked: PEffects, n: PNode) =
|
||||
case n.kind
|
||||
of nkRaiseStmt: throws(tracked, n)
|
||||
of nkCallNode:
|
||||
# p's effects are ours too:
|
||||
let op = n.sons[0].typ
|
||||
InternalAssert op.kind == tyProc and op.n.sons[0].kind == nkEffectList
|
||||
var effectList = op.n.sons[0]
|
||||
if effectList.len == 0:
|
||||
if isIndirectCall(n.sons[0]) or isForwardedProc(n.sons[0]):
|
||||
# assume the worst: raise of exception 'E_Base':
|
||||
var rs = newNodeI(nkRaiseStmt, n.info)
|
||||
var re = newNodeIT(nkType, n.info, sysTypeFromName"E_Base")
|
||||
rs.add(re)
|
||||
effectList.add(rs)
|
||||
mergeEffects(tracked, effectList)
|
||||
of nkTryStmt:
|
||||
trackTryStmt(tracked, n)
|
||||
return
|
||||
of nkPragma:
|
||||
trackPragmaStmt(tracked, n)
|
||||
return
|
||||
else: nil
|
||||
for i in 0 .. <safeLen(n):
|
||||
track(tracked, n.sons[i])
|
||||
|
||||
proc trackProc*(s: PSym, body: PNode) =
|
||||
var effects = s.typ.n.sons[0]
|
||||
InternalAssert effects.kind == nkEffectList
|
||||
# effects already computed?
|
||||
if effects.len == effectListLen: return
|
||||
newSeq(effects.sons, effectListLen)
|
||||
|
||||
var t: TEffects
|
||||
t.exc = effects.sons[exceptionEffects]
|
||||
track(t, body)
|
||||
|
||||
|
||||
@@ -647,7 +647,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
if genericParams != nil and sonsLen(genericParams) == 0:
|
||||
cl = initIntSet()
|
||||
rawAddSon(result, nil) # return type
|
||||
res = newNodeI(nkType, n.info)
|
||||
# result.n[0] used to be `nkType`, but now it's `nkEffectList` because
|
||||
# the effects are now stored in there too ... this is a bit hacky, but as
|
||||
# usual we desperately try to save memory:
|
||||
res = newNodeI(nkEffectList, n.info)
|
||||
addSon(result.n, res)
|
||||
var check = initIntSet()
|
||||
var counter = 0
|
||||
|
||||
@@ -59,6 +59,8 @@ proc setupEnvironment =
|
||||
#addFile(nimrodDir / r"tinyc\lib\libtcc1.c")
|
||||
else:
|
||||
addSysincludePath(gTinyC, "/usr/include")
|
||||
when defined(amd64):
|
||||
addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu")
|
||||
|
||||
proc compileCCode*(ccode: string) =
|
||||
if not libIncluded:
|
||||
@@ -66,11 +68,11 @@ proc compileCCode*(ccode: string) =
|
||||
setupEnvironment()
|
||||
discard compileString(gTinyC, ccode)
|
||||
|
||||
proc run*() =
|
||||
proc run*() =
|
||||
var a: array[0..1, cstring]
|
||||
a[0] = ""
|
||||
a[1] = ""
|
||||
var err = tinyc.run(gTinyC, 0'i32, addr(a)) != 0'i32
|
||||
var err = tinyc.run(gTinyC, 0'i32, cast[cstringArray](addr(a))) != 0'i32
|
||||
closeCCState(gTinyC)
|
||||
if err: rawMessage(errExecutionOfProgramFailed, "")
|
||||
if err: rawMessage(errExecutionOfProgramFailed, "")
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import
|
||||
|
||||
# Keywords must be kept sorted and within a range
|
||||
|
||||
type
|
||||
type
|
||||
TSpecialWord* = enum
|
||||
wInvalid,
|
||||
|
||||
@@ -52,7 +52,7 @@ type
|
||||
wBoundchecks, wOverflowchecks, wNilchecks,
|
||||
wFloatchecks, wNanChecks, wInfChecks,
|
||||
wAssertions, wPatterns, wWarnings,
|
||||
wHints, wOptimization, wSpeed, wSize, wNone,
|
||||
wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects,
|
||||
wDeadCodeElim, wSafecode,
|
||||
wPragma,
|
||||
wCompileTime, wNoInit,
|
||||
@@ -62,7 +62,7 @@ type
|
||||
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
|
||||
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
|
||||
wNoStackFrame,
|
||||
wImplicitStatic, wGlobal, wHoist
|
||||
wImplicitStatic, wGlobal, wHoist,
|
||||
|
||||
wAuto, wBool, wCatch, wChar, wClass,
|
||||
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
|
||||
@@ -133,7 +133,7 @@ const
|
||||
"floatchecks", "nanchecks", "infchecks",
|
||||
|
||||
"assertions", "patterns", "warnings", "hints",
|
||||
"optimization", "speed", "size", "none",
|
||||
"optimization", "raises", "writes", "reads", "size", "effects",
|
||||
"deadcodeelim", "safecode",
|
||||
"pragma",
|
||||
"compiletime", "noinit",
|
||||
|
||||
@@ -470,4 +470,6 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
|
||||
# define GC_GUARD
|
||||
#endif
|
||||
|
||||
typedef int assert_numbits[sizeof(NI) == sizeof(void*) &&
|
||||
NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
|
||||
#endif
|
||||
|
||||
@@ -24,6 +24,9 @@ when defined(Windows):
|
||||
import winlean
|
||||
else:
|
||||
import posix
|
||||
|
||||
when defined(solaris):
|
||||
{.passl: "-lsocket -lnsl".}
|
||||
|
||||
# Note: The enumerations are mapped to Window's constants.
|
||||
|
||||
|
||||
@@ -4,5 +4,5 @@ type
|
||||
|
||||
var a: TArray
|
||||
|
||||
echo a[0] #OUT 0
|
||||
echo a[0x0012] #OUT 0
|
||||
|
||||
|
||||
3
todo.txt
3
todo.txt
@@ -37,7 +37,8 @@ version 0.9.XX
|
||||
echo a
|
||||
echo b)
|
||||
|
||||
- implement the "snoopResult" pragma
|
||||
- implement the "snoopResult" pragma; no, make a strutils with string append
|
||||
semantics instead ...
|
||||
- implement "closure tuple consists of a single 'ref'" optimization
|
||||
- JS gen:
|
||||
- fix exception handling
|
||||
|
||||
Reference in New Issue
Block a user