diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 22 | ||||
-rw-r--r-- | compiler/astalgo.nim | 4 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 3 | ||||
-rw-r--r-- | compiler/cgen.nim | 2 | ||||
-rw-r--r-- | compiler/condsyms.nim | 3 | ||||
-rw-r--r-- | compiler/lambdalifting.nim | 31 | ||||
-rw-r--r-- | compiler/lexer.nim | 4 | ||||
-rw-r--r-- | compiler/lowerings.nim | 176 | ||||
-rw-r--r-- | compiler/msgs.nim | 10 | ||||
-rw-r--r-- | compiler/parser.nim | 3 | ||||
-rw-r--r-- | compiler/pragmas.nim | 35 | ||||
-rw-r--r-- | compiler/renderer.nim | 6 | ||||
-rw-r--r-- | compiler/semexprs.nim | 2 | ||||
-rw-r--r-- | compiler/sempass2.nim | 112 | ||||
-rw-r--r-- | compiler/semtypes.nim | 9 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 16 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 3 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 14 |
18 files changed, 368 insertions, 87 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 0652bd3e7..e575f317d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -13,7 +13,7 @@ import msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists, intsets, idgen -type +type TCallingConvention* = enum ccDefault, # proc has no explicit calling convention ccStdCall, # procedure is stdcall @@ -299,7 +299,7 @@ const 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 + usesEffects* = 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 @@ -432,7 +432,7 @@ type tfAcyclic, # type is acyclic (for GC optimization) tfEnumHasHoles, # enum cannot be mapped into a range tfShallow, # type can be shallow copied on assignment - tfThread, # proc type is marked as ``thread`` + tfThread, # proc type is marked as ``thread``; alias for ``gcsafe`` tfFromGeneric, # type is an instantiation of a generic; this is needed # because for instantiations of objects, structural # type equality has to be used @@ -509,6 +509,7 @@ const tfIncompleteStruct* = tfVarargs tfUncheckedArray* = tfVarargs tfUnion* = tfNoSideEffect + tfGcSafe* = tfThread skError* = skUnknown # type flags that are essential for type equality: @@ -559,7 +560,7 @@ type mFloat, mFloat32, mFloat64, mFloat128, mBool, mChar, mString, mCstring, mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc, - mVoidType, mPNimrodNode, + mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor, mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mAppType, mNaN, mInf, mNegInf, @@ -978,6 +979,8 @@ template `{}=`*(n: PNode, i: int, s: PNode): stmt = var emptyNode* = newNode(nkEmpty) # There is a single empty node that is shared! Do not overwrite it! +var anyGlobal* = newSym(skVar, getIdent("*"), nil, unknownLineInfo()) + proc isMetaType*(t: PType): bool = return t.kind in tyMetaTypes or (t.kind == tyStatic and t.n == nil) or @@ -1310,8 +1313,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType = proc propagateToOwner*(owner, elem: PType) = const HaveTheirOwnEmpty = {tySequence, tySet} - owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta, - tfHasGCedMem}) + owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta}) if tfNotNil in elem.flags: if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}: owner.flags.incl tfNotNil @@ -1328,9 +1330,11 @@ proc propagateToOwner*(owner, elem: PType) = if elem.isMetaType: owner.flags.incl tfHasMeta - if elem.kind in {tyString, tyRef, tySequence} or - elem.kind == tyProc and elem.callConv == ccClosure: - owner.flags.incl tfHasGCedMem + if owner.kind != tyProc: + if elem.kind in {tyString, tyRef, tySequence} or + elem.kind == tyProc and elem.callConv == ccClosure or + tfHasGCedMem in elem.flags: + owner.flags.incl tfHasGCedMem proc rawAddSon*(father, son: PType) = if isNil(father.sons): father.sons = @[] diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 36dd7f562..dbf13f764 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -448,9 +448,9 @@ proc debug(n: PSym) = writeln(stdout, "skUnknown") else: #writeln(stdout, ropeToStr(symToYaml(n, 0, 1))) - writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4", [ + writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4, $5", [ toRope(n.name.s), toRope(n.id), flagsToStr(n.flags), - flagsToStr(n.loc.flags)]))) + flagsToStr(n.loc.flags), lineInfoToStr(n.info)]))) proc debug(n: PType) = writeln(stdout, ropeToStr(debugType(n))) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 7c4cc2b80..49350fa9c 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1635,6 +1635,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = localError(e.info, errCannotGenerateCodeForX, e.sons[0].sym.name.s) of mSlurp..mQuoteAst: localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s) + of mSpawn: + let n = lowerings.wrapProcForSpawn(p.module.module, e.sons[1]) + expr(p, n, d) else: internalError(e.info, "genMagicExpr: " & $op) proc genConstExpr(p: BProc, n: PNode): PRope diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 683aed069..f64ebacfb 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -14,7 +14,7 @@ import options, intsets, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, - rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases + rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings when options.hasTinyCBackend: import tccgen diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index ddf2075b4..4117fc461 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -48,6 +48,7 @@ proc initDefines*() = defineSymbol("nimbabel") defineSymbol("nimcomputedgoto") defineSymbol("nimunion") + defineSymbol("nimnewshared") # add platform specific symbols: case targetCPU diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 440468ac4..c5b9d0f00 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -141,12 +141,6 @@ type closureParam, state, resultSym: PSym # only if isIter obj: PType # only if isIter -proc createObj(owner: PSym, info: TLineInfo): PType = - result = newType(tyObject, owner) - rawAddSon(result, nil) - incl result.flags, tfFinal - result.n = newNodeI(nkRecList, info) - proc getStateType(iter: PSym): PType = var n = newNodeI(nkRange, iter.info) addSon(n, newIntNode(nkIntLit, -1)) @@ -189,15 +183,6 @@ proc getEnvParam(routine: PSym): PSym = let hidden = lastSon(params) if hidden.kind == nkSym and hidden.sym.name.s == paramName: result = hidden.sym - -proc addField(obj: PType; s: PSym) = - # because of 'gensym' support, we have to mangle the name with its ID. - # This is hacky but the clean solution is much more complex than it looks. - var field = newSym(skField, getIdent(s.name.s & $s.id), s.owner, s.info) - let t = skipIntLit(s.typ) - field.typ = t - field.position = sonsLen(obj.n) - addSon(obj.n, newSymNode(field)) proc initIterContext(c: POuterContext, iter: PSym) = c.fn = iter @@ -273,22 +258,6 @@ proc addDep(e, d: PEnv, owner: PSym): PSym = rawAddSon(result.typ, d.obj) addField(e.obj, result) e.deps.add((d, result)) - -proc indirectAccess(a: PNode, b: PSym, info: TLineInfo): PNode = - # returns a[].b as a node - var deref = newNodeI(nkHiddenDeref, info) - deref.typ = a.typ.sons[0] - assert deref.typ.kind == tyObject - let field = getSymFromList(deref.typ.n, getIdent(b.name.s & $b.id)) - assert field != nil, b.name.s - addSon(deref, a) - result = newNodeI(nkDotExpr, info) - addSon(result, deref) - addSon(result, newSymNode(field)) - result.typ = field.typ - -proc indirectAccess(a, b: PSym, info: TLineInfo): PNode = - result = indirectAccess(newSymNode(a), b, info) proc newCall(a, b: PSym): PNode = result = newNodeI(nkCall, a.info) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 217e33675..2bfd8d1eb 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -43,7 +43,7 @@ type tkLambda, tkLet, tkMacro, tkMethod, tkMixin, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, - tkProc, tkPtr, tkRaise, tkRef, tkReturn, tkShared, tkShl, tkShr, tkStatic, + tkProc, tkPtr, tkRaise, tkRef, tkReturn, tkShl, tkShr, tkStatic, tkTemplate, tkTry, tkTuple, tkType, tkUsing, tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor, @@ -79,7 +79,7 @@ const "macro", "method", "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc", "ptr", "raise", "ref", "return", - "shared", "shl", "shr", "static", + "shl", "shr", "static", "template", "try", "tuple", "type", "using", "var", "when", "while", "with", "without", "xor", diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 2cf641d93..0ca07e828 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -12,7 +12,7 @@ const genPrefix* = ":tmp" # prefix for generated names -import ast, types, idents, magicsys +import ast, astalgo, types, idents, magicsys, msgs proc newTupleAccess*(tup: PNode, i: int): PNode = result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes( @@ -34,6 +34,11 @@ proc newAsgnStmt(le, ri: PNode): PNode = result.sons[0] = le result.sons[1] = ri +proc newFastAsgnStmt(le, ri: PNode): PNode = + result = newNodeI(nkFastAsgn, le.info, 2) + result.sons[0] = le + result.sons[1] = ri + proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode = assert n.kind == nkVarTuple let value = n.lastSon @@ -50,3 +55,172 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode = result.add newAsgnStmt(newSymNode(temp), value) for i in 0 .. n.len-3: result.add newAsgnStmt(n.sons[i], newTupleAccess(value, i)) + +proc createObj*(owner: PSym, info: TLineInfo): PType = + result = newType(tyObject, owner) + rawAddSon(result, nil) + incl result.flags, tfFinal + result.n = newNodeI(nkRecList, info) + +proc addField*(obj: PType; s: PSym) = + # because of 'gensym' support, we have to mangle the name with its ID. + # This is hacky but the clean solution is much more complex than it looks. + var field = newSym(skField, getIdent(s.name.s & $s.id), s.owner, s.info) + let t = skipIntLit(s.typ) + field.typ = t + field.position = sonsLen(obj.n) + addSon(obj.n, newSymNode(field)) + +proc newDotExpr(obj, b: PSym): PNode = + result = newNodeI(nkDotExpr, obj.info) + let field = getSymFromList(obj.typ.n, getIdent(b.name.s & $b.id)) + assert field != nil, b.name.s + addSon(result, newSymNode(obj)) + addSon(result, newSymNode(field)) + result.typ = field.typ + +proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode = + # returns a[].b as a node + var deref = newNodeI(nkHiddenDeref, info) + deref.typ = a.typ.sons[0] + assert deref.typ.kind == tyObject + let field = getSymFromList(deref.typ.n, getIdent(b.name.s & $b.id)) + assert field != nil, b.name.s + addSon(deref, a) + result = newNodeI(nkDotExpr, info) + addSon(result, deref) + addSon(result, newSymNode(field)) + result.typ = field.typ + +proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode = + result = indirectAccess(newSymNode(a), b, info) + +proc genAddrOf*(n: PNode): PNode = + result = newNodeI(nkAddr, n.info, 1) + result.sons[0] = n + result.typ = newType(tyPtr, n.typ.owner) + result.typ.rawAddSon(n.typ) + +proc callCodegenProc*(name: string, arg1: PNode; + arg2, arg3: PNode = nil): PNode = + result = newNodeI(nkCall, arg1.info) + let sym = magicsys.getCompilerProc(name) + if sym == nil: + localError(arg1.info, errSystemNeeds, name) + else: + result.add newSymNode(sym) + result.add arg1 + if arg2 != nil: result.add arg2 + if arg3 != nil: result.add arg3 + +proc createWrapperProc(f: PNode; threadParam, argsParam: PSym; + varSection, call: PNode): PSym = + var body = newNodeI(nkStmtList, f.info) + body.add varSection + body.add callCodeGenProc("nimArgsPassingDone", newSymNode(threadParam)) + body.add call + + var params = newNodeI(nkFormalParams, f.info) + params.add emptyNode + params.add threadParam.newSymNode + params.add argsParam.newSymNode + + var t = newType(tyProc, threadParam.owner) + t.rawAddSon nil + t.rawAddSon threadParam.typ + t.rawAddSon argsParam.typ + t.n = newNodeI(nkFormalParams, f.info) + t.n.add newNodeI(nkEffectList, f.info) + t.n.add threadParam.newSymNode + t.n.add argsParam.newSymNode + + let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper" + result = newSym(skProc, getIdent(name), argsParam.owner, f.info) + result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result)) + result.typ = t + +proc createCastExpr(argsParam: PSym; objType: PType): PNode = + result = newNodeI(nkCast, argsParam.info) + result.add emptyNode + result.add newSymNode(argsParam) + result.typ = newType(tyPtr, objType.owner) + result.typ.rawAddSon(objType) + +proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode = + result = newNodeI(nkStmtList, n.info) + if n.kind notin nkCallKinds or not n.typ.isEmptyType: + localError(n.info, "'spawn' takes a call expression of type void") + return + if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}: + localError(n.info, "'spawn' takes a GC safe call expression") + var + threadParam = newSym(skParam, getIdent"thread", owner, n.info) + argsParam = newSym(skParam, getIdent"args", owner, n.info) + block: + let ptrType = getSysType(tyPointer) + threadParam.typ = ptrType + argsParam.typ = ptrType + argsParam.position = 1 + var objType = createObj(owner, n.info) + let castExpr = createCastExpr(argsParam, objType) + + var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info) + block: + scratchObj.typ = objType + incl(scratchObj.flags, sfFromGeneric) + var varSectionB = newNodeI(nkVarSection, n.info) + varSectionB.addVar(scratchObj.newSymNode) + result.add varSectionB + + var call = newNodeI(nkCall, n.info) + var fn = n.sons[0] + # templates and macros are in fact valid here due to the nature of + # the transformation: + if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro, + skMethod, skConverter}): + # for indirect calls we pass the function pointer in the scratchObj + var argType = n[0].typ.skipTypes(abstractInst) + var field = newSym(skField, getIdent"fn", owner, n.info) + field.typ = argType + objType.addField(field) + result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0]) + fn = indirectAccess(castExpr, field, n.info) + elif fn.kind == nkSym and fn.sym.kind in {skClosureIterator, skIterator}: + localError(n.info, "iterator in spawn environment is not allowed") + elif fn.typ.callConv == ccClosure: + localError(n.info, "closure in spawn environment is not allowed") + + call.add(fn) + var varSection = newNodeI(nkVarSection, n.info) + let formals = n[0].typ.n + let tmpName = getIdent(genPrefix) + for i in 1 .. <n.len: + # we pick n's type here, which hopefully is 'tyArray' and not + # 'tyOpenArray': + var argType = n[i].typ.skipTypes(abstractInst) + if i < formals.len and formals[i].typ.kind == tyVar: + localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter") + elif containsTyRef(argType): + localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure") + + let fieldname = if i < formals.len: formals[i].sym.name else: tmpName + var field = newSym(skField, fieldname, owner, n.info) + field.typ = argType + objType.addField(field) + result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i]) + + var temp = newSym(skTemp, tmpName, owner, n.info) + temp.typ = argType + incl(temp.flags, sfFromGeneric) + + var vpart = newNodeI(nkIdentDefs, n.info, 3) + vpart.sons[0] = newSymNode(temp) + vpart.sons[1] = ast.emptyNode + vpart.sons[2] = indirectAccess(castExpr, field, n.info) + varSection.add vpart + + call.add(newSymNode(temp)) + + let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, call) + result.add callCodeGenProc("nimSpawn", wrapper.newSymNode, + genAddrOf(scratchObj.newSymNode)) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index b1d03e503..4d471bdac 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -118,7 +118,7 @@ type warnNilStatement, warnAnalysisLoophole, warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure, warnEachIdentIsTuple, warnShadowIdent, - warnProveInit, warnProveField, warnProveIndex, + warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnUninit, warnGcMem, warnUser, hintSuccess, hintSuccessX, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, @@ -386,6 +386,7 @@ const warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]", warnProveField: "cannot prove that field '$1' is accessible [ProveField]", warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]", + warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]", warnUninit: "'$1' might not have been initialized [Uninit]", warnGcMem: "'$1' uses GC'ed memory [GcMem]", warnUser: "$1 [User]", @@ -407,7 +408,7 @@ const hintUser: "$1 [User]"] const - WarningsToStr*: array[0..24, string] = ["CannotOpenFile", "OctalEscape", + WarningsToStr*: array[0..25, string] = ["CannotOpenFile", "OctalEscape", "XIsNeverRead", "XmightNotBeenInit", "Deprecated", "ConfigDeprecated", "SmallLshouldNotBeUsed", "UnknownMagic", @@ -415,7 +416,8 @@ const "CommentXIgnored", "NilStmt", "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", - "ProveInit", "ProveField", "ProveIndex", "Uninit", "GcMem", "User"] + "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "Uninit", + "GcMem", "User"] HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", @@ -557,7 +559,7 @@ proc sourceLine*(i: TLineInfo): PRope var gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, - warnProveField, warnProveIndex} + warnProveField, warnProveIndex, warnGcUnsafe} gErrorCounter*: int = 0 # counts the number of errors gHintCounter*: int = 0 gWarnCounter*: int = 0 diff --git a/compiler/parser.nim b/compiler/parser.nim index c68c80b46..5c7b86240 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -967,7 +967,7 @@ proc isExprStart(p: TParser): bool = of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkProc, tkIterator, tkBind, tkAddr, tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, - tkTuple, tkObject, tkType, tkWhen, tkCase, tkShared: + tkTuple, tkObject, tkType, tkWhen, tkCase: result = true else: result = false @@ -1040,7 +1040,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode) of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode) of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode) - of tkShared: result = parseTypeDescKAux(p, nkSharedTy, mode) of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode) of tkType: result = parseTypeDescKAux(p, nkTypeOfExpr, mode) of tkTuple: result = parseTuple(p, mode == pmTypeDef) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 14d155539..88fa516bf 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -24,7 +24,7 @@ const wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, - wGensym, wInject, wRaises, wTags, wOperator, wDelegator} + wGensym, wInject, wRaises, wTags, wUses, wOperator, wDelegator, wGcSafe} converterPragmas* = procPragmas methodPragmas* = procPragmas templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, @@ -35,7 +35,7 @@ const iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect, wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises, - wTags, wOperator} + wTags, wUses, wOperator, wGcSafe} exprPragmas* = {wLine} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, @@ -48,7 +48,7 @@ const lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, - wRaises, wTags} + wRaises, wUses, wTags, wGcSafe} typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, @@ -64,7 +64,7 @@ const wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, - wThread, wRaises, wTags} + wThread, wRaises, wUses, wTags, wGcSafe} allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) @@ -513,6 +513,27 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) = else: invalidPragma(n) +proc pragmaUses(c: PContext, n: PNode) = + proc processExc(c: PContext, x: PNode): PNode = + if x.kind in {nkAccQuoted, nkIdent, nkSym, + nkOpenSymChoice, nkClosedSymChoice}: + if considerAcc(x).s == "*": + return newSymNode(ast.anyGlobal) + result = c.semExpr(c, x) + if result.kind != nkSym or sfGlobal notin result.sym.flags: + localError(x.info, "'$1' is not a global variable" % result.renderTree) + result = newSymNode(ast.anyGlobal) + + if n.kind == nkExprColonExpr: + let it = n.sons[1] + if it.kind notin {nkCurly, nkBracket}: + n.sons[1] = processExc(c, it) + else: + for i in 0 .. <it.len: + it.sons[i] = processExc(c, it.sons[i]) + else: + invalidPragma(n) + proc typeBorrow(sym: PSym, n: PNode) = if n.kind == nkExprColonExpr: let it = n.sons[1] @@ -667,6 +688,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, incl(sym.flags, sfThread) incl(sym.flags, sfProcvar) if sym.typ != nil: incl(sym.typ.flags, tfThread) + of wGcSafe: + noVal(it) + incl(sym.flags, sfThread) + if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) + else: invalidPragma(it) of wPacked: noVal(it) if sym.typ == nil: invalidPragma(it) @@ -759,6 +785,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, if sym == nil: invalidPragma(it) of wLine: pragmaLine(c, it) of wRaises, wTags: pragmaRaisesOrTags(c, it) + of wUses: pragmaUses(c, it) of wOperator: if sym == nil: invalidPragma(it) else: sym.position = expectIntLit(c, it) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index fa119eba9..6b62c48c5 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1082,12 +1082,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[1]) else: put(g, tkIterator, "iterator") - of nkSharedTy: - if sonsLen(n) > 0: - putWithSpace(g, tkShared, "shared") - gsub(g, n.sons[0]) - else: - put(g, tkShared, "shared") of nkStaticTy: put(g, tkStatic, "static") put(g, tkBracketLe, "[") diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 6c1721bdd..652e1dd4b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -30,6 +30,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if result.typ != nil: # XXX tyGenericInst here? if result.typ.kind == tyVar: result = newDeref(result) + elif efWantStmt in flags: + result.typ = newTypeS(tyEmpty, c) else: localError(n.info, errExprXHasNoType, renderTree(result, {renderNoComments})) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index a00325277..b2b91490c 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -59,15 +59,19 @@ discard """ --> we need a stack of scopes for this analysis """ +const trackGlobals = false ## we don't need it for now + type TEffects = object exc: PNode # stack of exceptions tags: PNode # list of tags + uses: PNode # list of used global variables bottom: int owner: PSym init: seq[int] # list of initialized variables guards: TModel # nested guards locked: seq[PNode] # locked locations + gcUnsafe: bool PEffects = var TEffects proc isLocalVar(a: PEffects, s: PSym): bool = @@ -89,20 +93,29 @@ proc initVarViaNew(a: PEffects, n: PNode) = # are initialized: initVar(a, n) +when trackGlobals: + proc addUse(a: PEffects, e: PNode) = + var aa = a.uses + for i in 0 .. <aa.len: + if aa[i].sym.id == e.sym.id: return + a.uses.add(e) + proc useVar(a: PEffects, n: PNode) = let s = n.sym if isLocalVar(a, s): if s.id notin a.init: if {tfNeedsInit, tfNotNil} * s.typ.flags != {}: - when true: - message(n.info, warnProveInit, s.name.s) - else: - Message(n.info, errGenerated, - "'$1' might not have been initialized" % s.name.s) + message(n.info, warnProveInit, s.name.s) else: message(n.info, warnUninit, s.name.s) # prevent superfluous warnings about the same variable: a.init.add s.id + if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind == skVar: + when trackGlobals: + a.addUse(copyNode(n)) + if tfHasGCedMem in s.typ.flags: + message(n.info, warnGcUnsafe, renderTree(n)) + a.gcUnsafe = true type TIntersection = seq[tuple[id, count: int]] # a simple count table @@ -132,6 +145,10 @@ proc createTag(n: PNode): PNode = result.typ = sysTypeFromName"TEffect" if not n.isNil: result.info = n.info +proc createAnyGlobal(n: PNode): PNode = + result = newSymNode(anyGlobal) + result.info = n.info + proc addEffect(a: PEffects, e: PNode, useLineInfo=true) = assert e.kind != nkRaiseStmt var aa = a.exc @@ -161,9 +178,17 @@ proc mergeTags(a: PEffects, b, comesFrom: PNode) = else: for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil) +when trackGlobals: + proc mergeUses(a: PEffects, b, comesFrom: PNode) = + if b.isNil: + addUse(a, createAnyGlobal(comesFrom)) + else: + for effect in items(b): addUse(a, effect) + proc listEffects(a: PEffects) = for e in items(a.exc): message(e.info, hintUser, typeToString(e.typ)) for e in items(a.tags): message(e.info, hintUser, typeToString(e.typ)) + for e in items(a.uses): message(e.info, hintUser, e.sym.name.s) proc catches(tracked: PEffects, e: PType) = let e = skipTypes(e, skipPtrs) @@ -289,6 +314,13 @@ proc documentRaises*(n: PNode) = if n.sons[namePos].kind != nkSym: return documentEffect(n, n.sons[pragmasPos], wRaises, exceptionEffects) documentEffect(n, n.sons[pragmasPos], wTags, tagEffects) + documentEffect(n, n.sons[pragmasPos], wUses, usesEffects) + +template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {} + +proc importedFromC(n: PNode): bool = + # when imported from C, we assume GC-safety. + result = n.kind == nkSym and sfImportc in n.sym.flags proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = let pragma = s.ast.sons[pragmasPos] @@ -298,6 +330,14 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = let tagSpec = effectSpec(pragma, wTags) mergeTags(tracked, tagSpec, n) + if notGcSafe(s.typ) and sfImportc notin s.flags: + message(n.info, warnGcUnsafe, renderTree(n)) + tracked.gcUnsafe = true + + when trackGlobals: + let usesSpec = effectSpec(pragma, wUses) + mergeUses(tracked, usesSpec, n) + proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = let n = n.skipConv if paramType != nil and tfNotNil in paramType.flags and @@ -330,9 +370,18 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = else: addEffect(tracked, createRaise(n)) addTag(tracked, createTag(n)) + when trackGlobals: addUse(tracked, createAnyGlobal(n)) + # assume GcUnsafe unless in its type: + if notGcSafe(op): + message(n.info, warnGcUnsafe, renderTree(n)) + tracked.gcUnsafe = true else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) + when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n) + if notGcSafe(op): + message(n.info, warnGcUnsafe, renderTree(n)) + tracked.gcUnsafe = true notNilCheck(tracked, n, paramType) proc breaksBlock(n: PNode): bool = @@ -451,8 +500,11 @@ proc track(tracked: PEffects, n: PNode) = # XXX: in rare situations, templates and macros will reach here after # calling getAst(templateOrMacro()). Currently, templates and macros # are indistinguishable from normal procs (both have tyProc type) and - # we can detect them only by cheking for attached nkEffectList. + # we can detect them only by checking for attached nkEffectList. if op != nil and op.kind == tyProc and op.n.sons[0].kind == nkEffectList: + if notGcSafe(op) and not importedFromC(a): + message(n.info, warnGcUnsafe, renderTree(n)) + tracked.gcUnsafe = true var effectList = op.n.sons[0] if a.kind == nkSym and a.sym.kind == skMethod: propagateEffects(tracked, n, a.sym) @@ -462,9 +514,11 @@ proc track(tracked: PEffects, n: PNode) = elif isIndirectCall(a, tracked.owner): addEffect(tracked, createRaise(n)) addTag(tracked, createTag(n)) + when trackGlobals: addUse(tracked, createAnyGlobal(n)) else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) + when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n) for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}: # may not look like an assignment, but it is: @@ -531,14 +585,21 @@ proc track(tracked: PEffects, n: PNode) = else: for i in 0 .. <safeLen(n): track(tracked, n.sons[i]) -proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool) = +proc subtypeRelation(spec, real: PNode): bool = + result = safeInheritanceDiff(real.excType, spec.typ) <= 0 + +proc symbolPredicate(spec, real: PNode): bool = + result = real.sym.id == spec.sym.id + +proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool; + effectPredicate: proc (a, b: PNode): bool {.nimcall.}) = # check that any real exception is listed in 'spec'; mark those as used; # report any unused exception var used = initIntSet() for r in items(real): block search: for s in 0 .. <spec.len: - if safeInheritanceDiff(r.excType, spec[s].typ) <= 0: + if effectPredicate(spec[s], r): used.incl(s) break search # XXX call graph analysis would be nice here! @@ -560,11 +621,18 @@ proc checkMethodEffects*(disp, branch: PSym) = let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): checkRaisesSpec(raisesSpec, actual.sons[exceptionEffects], - "can raise an unlisted exception: ", hints=off) + "can raise an unlisted exception: ", hints=off, subtypeRelation) let tagsSpec = effectSpec(p, wTags) if not isNil(tagsSpec): checkRaisesSpec(tagsSpec, actual.sons[tagEffects], - "can have an unlisted effect: ", hints=off) + "can have an unlisted effect: ", hints=off, subtypeRelation) + let usesSpec = effectSpec(p, wUses) + if not isNil(usesSpec): + checkRaisesSpec(usesSpec, actual.sons[usesEffects], + "may use an unlisted global variable: ", hints=off, symbolPredicate) + if sfThread in disp.flags and notGcSafe(branch.typ): + localError(branch.info, "base method is GC-safe, but '$1' is not" % + branch.name.s) proc setEffectsForProcType*(t: PType, n: PNode) = var effects = t.n.sons[0] @@ -573,21 +641,26 @@ proc setEffectsForProcType*(t: PType, n: PNode) = let raisesSpec = effectSpec(n, wRaises) tagsSpec = effectSpec(n, wTags) - if not isNil(raisesSpec) or not isNil(tagsSpec): + usesSpec = effectSpec(n, wUses) + if not isNil(raisesSpec) or not isNil(tagsSpec) or not isNil(usesSpec): internalAssert effects.len == 0 newSeq(effects.sons, effectListLen) if not isNil(raisesSpec): effects.sons[exceptionEffects] = raisesSpec if not isNil(tagsSpec): effects.sons[tagEffects] = tagsSpec + if not isNil(usesSpec): + effects.sons[usesEffects] = usesSpec proc initEffects(effects: PNode; s: PSym; t: var TEffects) = newSeq(effects.sons, effectListLen) effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info) effects.sons[tagEffects] = newNodeI(nkArgList, s.info) + effects.sons[usesEffects] = newNodeI(nkArgList, s.info) t.exc = effects.sons[exceptionEffects] t.tags = effects.sons[tagEffects] + t.uses = effects.sons[usesEffects] t.owner = s t.init = @[] t.guards = @[] @@ -602,7 +675,6 @@ proc trackProc*(s: PSym, body: PNode) = var t: TEffects initEffects(effects, s, t) track(t, body) - if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and s.kind in {skProc, skConverter, skMethod}: var res = s.ast.sons[resultPos].sym # get result symbol @@ -612,17 +684,27 @@ proc trackProc*(s: PSym, body: PNode) = let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): checkRaisesSpec(raisesSpec, t.exc, "can raise an unlisted exception: ", - hints=on) + hints=on, subtypeRelation) # after the check, use the formal spec: effects.sons[exceptionEffects] = raisesSpec let tagsSpec = effectSpec(p, wTags) if not isNil(tagsSpec): checkRaisesSpec(tagsSpec, t.tags, "can have an unlisted effect: ", - hints=off) + hints=off, subtypeRelation) # after the check, use the formal spec: effects.sons[tagEffects] = tagsSpec - + + when trackGlobals: + let usesSpec = effectSpec(p, wUses) + if not isNil(usesSpec): + checkRaisesSpec(usesSpec, t.uses, + "uses an unlisted global variable: ", hints=on, symbolPredicate) + effects.sons[usesEffects] = usesSpec + if sfThread in s.flags and t.gcUnsafe: + localError(s.info, "'$1' is not GC-safe" % s.name.s) + if not t.gcUnsafe: s.typ.flags.incl tfGcSafe + proc trackTopLevelStmt*(module: PSym; n: PNode) = if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 5e391cc93..384bdc8a3 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1277,6 +1277,15 @@ proc processMagicType(c: PContext, m: PSym) = setMagicType(m, tyOrdinal, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) of mPNimrodNode: discard + of mShared: + setMagicType(m, tyObject, 0) + m.typ.n = newNodeI(nkRecList, m.info) + incl m.typ.flags, tfShared + of mGuarded: + setMagicType(m, tyObject, 0) + m.typ.n = newNodeI(nkRecList, m.info) + incl m.typ.flags, tfShared + rawAddSon(m.typ, sysTypeFromName"shared") else: localError(m.info, errTypeExpected) proc semGenericConstraints(c: PContext, x: PType): PType = diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 4a8a463f5..8b9b7b13b 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -14,11 +14,22 @@ import ast, astalgo, msgs, types, magicsys, semdata, renderer const tfInstClearedFlags = {tfHasMeta} +proc sharedPtrCheck(info: TLineInfo, t: PType) = + if t.kind == tyPtr and t.len > 1: + if t.sons[0].sym.magic in {mShared, mGuarded}: + incl(t.flags, tfShared) + if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded) + if tfHasGCedMem in t.flags: + localError(info, errGenerated, + "shared memory may not refer to GC'ed thread local memory") + proc checkPartialConstructedType(info: TLineInfo, t: PType) = if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: localError(info, errInvalidPragmaX, "acyclic") elif t.kind == tyVar and t.sons[0].kind == tyVar: localError(info, errVarVarTypeNotAllowed) + else: + sharedPtrCheck(info, t) proc checkConstructedType*(info: TLineInfo, typ: PType) = var t = typ.skipTypes({tyDistinct}) @@ -29,7 +40,8 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) = localError(info, errVarVarTypeNotAllowed) elif computeSize(t) == szIllegalRecursion: localError(info, errIllegalRecursionInTypeX, typeToString(t)) - + else: + sharedPtrCheck(info, t) when false: if t.kind == tyObject and t.sons[0] != nil: if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 9d1585c56..5d3ed05f7 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -402,7 +402,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: return isNone elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}: - # noSideEffect implies ``tfThread``! XXX really? + # noSideEffect implies ``tfThread``! return isNone elif f.flags * {tfIterator} != a.flags * {tfIterator}: return isNone @@ -1284,6 +1284,7 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode = result = a elif a.typ.isNil: let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator} + elif formal.kind == tyStmt: {efDetermineType, efWantStmt} else: {efDetermineType} result = c.semOperand(c, a, flags) else: diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 9fdb3bac6..fbe005031 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -30,7 +30,7 @@ type wInclude, wInterface, wIs, wIsnot, wIterator, wLambda, wLet, wMacro, wMethod, wMixin, wMod, wNil, wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn, - wShared, wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar, + wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar, wWhen, wWhile, wWith, wWithout, wXor, wYield, wColon, wColonColon, wEquals, wDot, wDotDot, @@ -44,7 +44,8 @@ type wImportCompilerProc, wImportc, wExportc, wIncompleteStruct, wRequiresInit, wAlign, wNodecl, wPure, wSideeffect, wHeader, - wNosideeffect, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar, + wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib, + wCompilerproc, wProcVar, wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wLink, wCompile, wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger, @@ -63,7 +64,7 @@ type wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wAsmNoStackFrame, - wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, + wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wUses, wAuto, wBool, wCatch, wChar, wClass, wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, @@ -110,7 +111,7 @@ const "macro", "method", "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc", "ptr", "raise", "ref", "return", - "shared", "shl", "shr", "static", + "shl", "shr", "static", "template", "try", "tuple", "type", "using", "var", "when", "while", "with", "without", "xor", "yield", @@ -125,7 +126,7 @@ const "importcpp", "importobjc", "importcompilerproc", "importc", "exportc", "incompletestruct", "requiresinit", "align", "nodecl", "pure", "sideeffect", - "header", "nosideeffect", "noreturn", "merge", "lib", "dynlib", + "header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib", "compilerproc", "procvar", "fatal", "error", "warning", "hint", "line", "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace", "link", "compile", "linksys", "deprecated", "varargs", @@ -146,6 +147,7 @@ const "computedgoto", "injectstmt", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked", + "guard", "uses", "auto", "bool", "catch", "char", "class", "const_cast", "default", "delete", "double", |