diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 6 | ||||
-rw-r--r-- | compiler/ccgcalls.nim | 4 | ||||
-rw-r--r-- | compiler/commands.nim | 3 | ||||
-rw-r--r-- | compiler/condsyms.nim | 1 | ||||
-rw-r--r-- | compiler/docgen.nim | 7 | ||||
-rw-r--r-- | compiler/lineinfos.nim | 2 | ||||
-rw-r--r-- | compiler/nim.cfg | 5 | ||||
-rw-r--r-- | compiler/options.nim | 3 | ||||
-rw-r--r-- | compiler/pragmas.nim | 30 | ||||
-rw-r--r-- | compiler/sem.nim | 7 | ||||
-rw-r--r-- | compiler/semcall.nim | 2 | ||||
-rw-r--r-- | compiler/semdata.nim | 4 | ||||
-rw-r--r-- | compiler/sempass2.nim | 116 | ||||
-rw-r--r-- | compiler/semstmts.nim | 9 | ||||
-rw-r--r-- | compiler/types.nim | 17 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 2 |
16 files changed, 164 insertions, 54 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 5893a5671..50d048edd 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -229,7 +229,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # 47 flags! + TSymFlag* = enum # 48 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -299,6 +299,7 @@ type sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally' sfSingleUsedTemp # For temporaries that we know will only be used once sfNoalias # 'noalias' annotation, means C's 'restrict' + sfEffectsDelayed # an 'effectsDelayed' parameter TSymFlags* = set[TSymFlag] @@ -568,6 +569,7 @@ type # sizeof, alignof, offsetof at CT tfExplicitCallConv tfIsConstructor + tfEffectSystemWorkaround TTypeFlags* = set[TTypeFlag] @@ -1781,7 +1783,7 @@ proc containsNode*(n: PNode, kinds: TNodeKinds): bool = proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool = case n.kind - of nkEmpty..nkNilLit: result = n.kind == kind + of nkEmpty..nkNilLit, nkFormalParams: result = n.kind == kind else: for i in 0..<n.len: if (n[i].kind == kind) or hasSubnodeWith(n[i], kind): diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 749a7a3b6..828e666a8 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -325,7 +325,7 @@ proc skipTrivialIndirections(n: PNode): PNode = proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) = case n.kind: - of nkLiterals, nkIdent: discard + of nkLiterals, nkIdent, nkFormalParams: discard of nkSym: if mutate: result.add n of nkAsgn, nkFastAsgn: @@ -354,7 +354,7 @@ proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) = proc getPotentialReads(n: PNode; result: var seq[PNode]) = case n.kind: - of nkLiterals, nkIdent: discard + of nkLiterals, nkIdent, nkFormalParams: discard of nkSym: result.add n else: for s in n: diff --git a/compiler/commands.nim b/compiler/commands.nim index 079717862..9b7e35791 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -8,7 +8,6 @@ # # This module handles the parsing of command line arguments. -from ast import setUseIc # We do this here before the 'import' statement so 'defined' does not get # confused with 'TGCMode.gcMarkAndSweep' etc. @@ -30,7 +29,7 @@ import msgs, options, nversion, condsyms, extccomp, platform, wordrecg, nimblecmd, lineinfos, pathutils, pathnorm -from ast import eqTypeFlags, tfGcSafe, tfNoSideEffect +from ast import setUseIc, eqTypeFlags, tfGcSafe, tfNoSideEffect # but some have deps to imported modules. Yay. bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc") diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 33cd1818b..df355bc26 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -137,3 +137,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasDragonBox") defineSymbol("nimHasHintAll") defineSymbol("nimHasTrace") + defineSymbol("nimHasEffectsOf") diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 706b54bcd..1acfc7489 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1085,10 +1085,11 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id let actual = s.typ.n[0] if actual.len != effectListLen: return let real = actual[idx] - + if real == nil: return + let realLen = real.len # warning: hack ahead: - var effects = newNodeI(nkBracket, n.info, real.len) - for i in 0..<real.len: + var effects = newNodeI(nkBracket, n.info, realLen) + for i in 0..<realLen: var t = typeToString(real[i].typ) if t.startsWith("ref "): t = substr(t, 4) effects[i] = newIdentNode(getIdent(cache, t), n.info) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index fd7ce0c04..8bd5a0890 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -75,6 +75,7 @@ type warnAnyEnumConv = "AnyEnumConv", warnHoleEnumConv = "HoleEnumConv", warnCstringConv = "CStringConv", + warnEffect = "Effect", warnUser = "User", # hints hintSuccess = "Success", hintSuccessX = "SuccessX", @@ -163,6 +164,7 @@ const warnAnyEnumConv: "$1", warnHoleEnumConv: "$1", warnCstringConv: "$1", + warnEffect: "$1", warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` diff --git a/compiler/nim.cfg b/compiler/nim.cfg index ec51bd463..d6ac6a937 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -25,3 +25,8 @@ define:useStdoutAsStdmsg @if nimHasWarningObservableStores: warning:ObservableStores: off @end + +@if nimHasEffectsOf: + experimental:strictEffects + warningAsError:Effect:on +@end diff --git a/compiler/options.nim b/compiler/options.nim index e37dea1b6..89a16a49c 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -204,7 +204,8 @@ type strictFuncs, views, strictNotNil, - overloadableEnums + overloadableEnums, + strictEffects LegacyFeature* = enum allowSemcheckedAstModification, diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 6637a8673..e9f52c71f 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -29,7 +29,7 @@ const wCompilerProc, wNonReloadable, wCore, wProcVar, wVarargs, wCompileTime, wMerge, wBorrow, wImportCompilerProc, wThread, wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl, - wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, + wGensym, wInject, wRaises, wEffectsOf, wTags, wLocks, wDelegator, wGcSafe, wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy, wRequires, wEnsures} converterPragmas* = procPragmas @@ -41,7 +41,7 @@ const wDiscardable, wGensym, wInject, wDelegator} iteratorPragmas* = declPragmas + {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect, wMagic, wBorrow, - wDiscardable, wGensym, wInject, wRaises, + wDiscardable, wGensym, wInject, wRaises, wEffectsOf, wTags, wLocks, wGcSafe, wRequires, wEnsures} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks, @@ -59,7 +59,7 @@ const lambdaPragmas* = {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, wThread, wAsmNoStackFrame, - wRaises, wLocks, wTags, wRequires, wEnsures, + wRaises, wLocks, wTags, wRequires, wEnsures, wEffectsOf, wGcSafe, wCodegenDecl, wNoInit, wCompileTime} typePragmas* = declPragmas + {wMagic, wAcyclic, wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow, @@ -79,7 +79,7 @@ const paramPragmas* = {wNoalias, wInject, wGensym} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, - wThread, wRaises, wLocks, wTags, wGcSafe, + wThread, wRaises, wEffectsOf, wLocks, wTags, wGcSafe, wRequires, wEnsures} forVarPragmas* = {wInject, wGensym} allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas @@ -779,6 +779,26 @@ proc semCustomPragma(c: PContext, n: PNode): PNode = # pragma(arg) -> pragma: arg result.transitionSonsKind(n.kind) +proc processEffectsOf(c: PContext, n: PNode; owner: PSym) = + proc processParam(c: PContext; n: PNode) = + let r = c.semExpr(c, n) + if r.kind == nkSym and r.sym.kind == skParam: + if r.sym.owner == owner: + incl r.sym.flags, sfEffectsDelayed + else: + localError(c.config, n.info, errGenerated, "parameter cannot be declared as .effectsOf") + else: + localError(c.config, n.info, errGenerated, "parameter name expected") + + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errGenerated, "parameter name expected") + else: + let it = n[1] + if it.kind in {nkCurly, nkBracket}: + for x in items(it): processParam(c, x) + else: + processParam(c, it) + proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, validPragmas: TSpecialWords, comesFromPush, isStatement: bool): bool = @@ -895,6 +915,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wNoalias: noVal(c, it) incl(sym.flags, sfNoalias) + of wEffectsOf: + processEffectsOf(c, it, sym) of wThreadVar: noVal(c, it) incl(sym.flags, {sfThread, sfGlobal}) diff --git a/compiler/sem.nim b/compiler/sem.nim index 804325e56..24709cf21 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -400,9 +400,10 @@ when not defined(nimHasSinkInference): include hlo, seminst, semcall proc resetSemFlag(n: PNode) = - excl n.flags, nfSem - for i in 0..<n.safeLen: - resetSemFlag(n[i]) + if n != nil: + excl n.flags, nfSem + for i in 0..<n.safeLen: + resetSemFlag(n[i]) proc semAfterMacroCall(c: PContext, call, macroResult: PNode, s: PSym, flags: TExprFlags): PNode = diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 2478420f7..a3064788e 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -152,6 +152,8 @@ proc effectProblem(f, a: PType; result: var string; c: PContext) = of efLockLevelsDiffer: result.add "\n The `.locks` requirements differ. Annotate the " & "proc with {.locks: 0.} to get extended error information." + of efEffectsDelayed: + result.add "\n The `.effectsOf` annotations differ." when defined(drnim): if not c.graph.compatibleProps(c.graph, f, a): result.add "\n The `.requires` or `.ensures` properties are incompatible." diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 0a6d09a56..31affb24f 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -261,7 +261,9 @@ proc getGenSym*(c: PContext; s: PSym): PSym = result = s proc considerGenSyms*(c: PContext; n: PNode) = - if n.kind == nkSym: + if n == nil: + discard "can happen for nkFormalParams/nkArgList" + elif n.kind == nkSym: let s = getGenSym(c, n.sym) if n.sym != s: n.sym = s diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index e2f85dd38..b584bb232 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -296,11 +296,12 @@ proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) = if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and s.magic != mNimvm: if s.guard != nil: guardGlobal(a, n, s.guard) - if {sfGlobal, sfThread} * s.flags == {sfGlobal} and - (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): - #if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n) - markGcUnsafe(a, s) - markSideEffect(a, s, n.info) + if strictEffects notin a.c.features: + if {sfGlobal, sfThread} * s.flags == {sfGlobal} and + (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): + #if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n) + markGcUnsafe(a, s) + markSideEffect(a, s, n.info) if s.owner != a.owner and s.kind in {skVar, skLet, skForVar, skResult, skParam} and {sfGlobal, sfThread} * s.flags == {}: a.isInnerProc = true @@ -474,14 +475,20 @@ proc trackTryStmt(tracked: PEffects, n: PNode) = for id, count in items(inter): if count == branches: tracked.init.add id -proc isIndirectCall(n: PNode, owner: PSym): bool = +proc isIndirectCall(tracked: PEffects; n: PNode): bool = # we don't count f(...) as an indirect call if 'f' is an parameter. # Instead we track expressions of type tyProc too. See the manual for # details: if n.kind != nkSym: result = true elif n.sym.kind == skParam: - result = owner != n.sym.owner or owner == nil + if strictEffects in tracked.c.features: + if tracked.owner == n.sym.owner and sfEffectsDelayed in n.sym.flags: + result = false # it is not a harmful call + else: + result = true + else: + result = tracked.owner != n.sym.owner or tracked.owner == nil elif n.sym.kind notin routineKinds: result = true @@ -579,9 +586,14 @@ proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) = # message(??.config, n.info, warnUser, "had to assume the worst here") mergeLockLevels(tracked, n, lockLevel) -proc isOwnedProcVar(n: PNode; owner: PSym): bool = +proc isOwnedProcVar(tracked: PEffects; n: PNode): bool = # XXX prove the soundness of this effect system rule - result = n.kind == nkSym and n.sym.kind == skParam and owner == n.sym.owner + result = n.kind == nkSym and n.sym.kind == skParam and + tracked.owner == n.sym.owner + #if result and sfPolymorphic notin n.sym.flags: + # echo tracked.config $ n.info, " different here!" + if strictEffects in tracked.c.features: + result = result and sfEffectsDelayed in n.sym.flags proc isNoEffectList(n: PNode): bool {.inline.} = assert n.kind == nkEffectList @@ -590,11 +602,15 @@ proc isNoEffectList(n: PNode): bool {.inline.} = proc isTrival(caller: PNode): bool {.inline.} = result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved, mSwap} -proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) = +proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; argIndex: int; caller: PNode) = let a = skipConvCastAndClosure(n) let op = a.typ + let param = if formals != nil and argIndex < formals.len and formals.n != nil: formals.n[argIndex].sym else: nil # assume indirect calls are taken here: - if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and not isTrival(caller): + if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and + not isTrival(caller) and + ((param != nil and sfEffectsDelayed in param.flags) or strictEffects notin tracked.c.features): + internalAssert tracked.config, op.n[0].kind == nkEffectList var effectList = op.n[0] var s = n.skipConv @@ -607,14 +623,14 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType; # we have no explicit effects but it's a forward declaration and so it's # stated there are no additional effects, so simply propagate them: propagateEffects(tracked, n, n.sym) - elif not isOwnedProcVar(a, tracked.owner): + elif not isOwnedProcVar(tracked, a): # we have no explicit effects so assume the worst: assumeTheWorst(tracked, n, op) # assume GcUnsafe unless in its type; 'forward' does not matter: - if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner): + if notGcSafe(op) and not isOwnedProcVar(tracked, a): if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config) markGcUnsafe(tracked, a) - elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner): + elif tfNoSideEffect notin op.flags and not isOwnedProcVar(tracked, a): markSideEffect(tracked, a, n.info) else: mergeRaises(tracked, effectList[exceptionEffects], n) @@ -624,6 +640,7 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType; markGcUnsafe(tracked, a) elif tfNoSideEffect notin op.flags: markSideEffect(tracked, a, n.info) + let paramType = if formals != nil and argIndex < formals.len: formals[argIndex] else: nil if paramType != nil and paramType.kind in {tyVar}: invalidateFacts(tracked.guards, n) if n.kind == nkSym and isLocalVar(tracked, n.sym): @@ -724,9 +741,6 @@ proc trackBlock(tracked: PEffects, n: PNode) = else: track(tracked, n) -proc paramType(op: PType, i: int): PType = - if op != nil and i < op.len: result = op[i] - proc cstringCheck(tracked: PEffects; n: PNode) = if n[0].typ.kind == tyCstring and (let a = skipConv(n[1]); a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}): @@ -770,6 +784,25 @@ proc checkRange(c: PEffects; value: PNode; typ: PType) = checkLe(c, lowBound, value) checkLe(c, value, highBound) +#[ +proc passedToEffectsDelayedParam(tracked: PEffects; n: PNode) = + let t = n.typ.skipTypes(abstractInst) + if t.kind == tyProc: + if n.kind == nkSym and tracked.owner == n.sym.owner and sfEffectsDelayed in n.sym.flags: + discard "the arg is itself a delayed parameter, so do nothing" + else: + var effectList = t.n[0] + if effectList.len == effectListLen: + mergeRaises(tracked, effectList[exceptionEffects], n) + mergeTags(tracked, effectList[tagEffects], n) + if not importedFromC(n): + if notGcSafe(t): + if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config) + markGcUnsafe(tracked, n) + if tfNoSideEffect notin t.flags: + markSideEffect(tracked, n, n.info) +]# + proc trackCall(tracked: PEffects; n: PNode) = template gcsafeAndSideeffectCheck() = if notGcSafe(op) and not importedFromC(a): @@ -811,7 +844,7 @@ proc trackCall(tracked: PEffects; n: PNode) = elif isNoEffectList(effectList): if isForwardedProc(a): propagateEffects(tracked, n, a.sym) - elif isIndirectCall(a, tracked.owner): + elif isIndirectCall(tracked, a): assumeTheWorst(tracked, n, op) gcsafeAndSideeffectCheck() else: @@ -819,7 +852,8 @@ proc trackCall(tracked: PEffects; n: PNode) = mergeTags(tracked, effectList[tagEffects], n) gcsafeAndSideeffectCheck() if a.kind != nkSym or a.sym.magic != mNBindSym: - for i in 1..<n.len: trackOperandForIndirectCall(tracked, n[i], paramType(op, i), a) + for i in 1..<n.len: + trackOperandForIndirectCall(tracked, n[i], op, i, a) if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}: # may not look like an assignment, but it is: let arg = n[1] @@ -1225,7 +1259,7 @@ proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool = else: return safeInheritanceDiff(g.excType(real), spec.typ) <= 0 -proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool; +proc checkRaisesSpec(g: ModuleGraph; emitWarnings: bool; spec, real: PNode, msg: string, hints: bool; effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.}; hintsArg: PNode = nil) = # check that any real exception is listed in 'spec'; mark those as used; @@ -1241,7 +1275,8 @@ proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool pushInfoContext(g.config, spec.info) var rr = if r.kind == nkRaiseStmt: r[0] else: r while rr.kind in {nkStmtList, nkStmtListExpr} and rr.len > 0: rr = rr.lastSon - localError(g.config, r.info, errGenerated, renderTree(rr) & " " & msg & typeToString(r.typ)) + message(g.config, r.info, if emitWarnings: warnEffect else: errGenerated, + renderTree(rr) & " " & msg & typeToString(r.typ)) popInfoContext(g.config) # hint about unnecessarily listed exception types: if hints: @@ -1258,11 +1293,11 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) = let p = disp.ast[pragmasPos] let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): - checkRaisesSpec(g, raisesSpec, actual[exceptionEffects], + checkRaisesSpec(g, false, raisesSpec, actual[exceptionEffects], "can raise an unlisted exception: ", hints=off, subtypeRelation) let tagsSpec = effectSpec(p, wTags) if not isNil(tagsSpec): - checkRaisesSpec(g, tagsSpec, actual[tagEffects], + checkRaisesSpec(g, false, tagsSpec, actual[tagEffects], "can have an unlisted effect: ", hints=off, subtypeRelation) if sfThread in disp.flags and notGcSafe(branch.typ): localError(g.config, branch.info, "base method is GC-safe, but '$1' is not" % @@ -1283,7 +1318,7 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) = "base method has lock level $1, but dispatcher has $2" % [$branch.typ.lockLevel, $disp.typ.lockLevel]) -proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) = +proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode; s: PSym = nil) = var effects = t.n[0] if t.kind != tyProc or effects.kind != nkEffectList: return if n.kind != nkEmpty: @@ -1292,9 +1327,14 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) = let raisesSpec = effectSpec(n, wRaises) if not isNil(raisesSpec): effects[exceptionEffects] = raisesSpec + elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}): + effects[exceptionEffects] = newNodeI(nkArgList, effects.info) + let tagsSpec = effectSpec(n, wTags) if not isNil(tagsSpec): effects[tagEffects] = tagsSpec + elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}): + effects[tagEffects] = newNodeI(nkArgList, effects.info) let requiresSpec = propSpec(n, wRequires) if not isNil(requiresSpec): @@ -1304,15 +1344,21 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) = effects[ensuresEffects] = ensuresSpec effects[pragmasEffects] = n + if s != nil and s.magic != mNone: + if s.magic != mEcho: + t.flags.incl tfNoSideEffect -proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) = +proc rawInitEffects(g: ModuleGraph; effects: PNode) = newSeq(effects.sons, effectListLen) - effects[exceptionEffects] = newNodeI(nkArgList, s.info) - effects[tagEffects] = newNodeI(nkArgList, s.info) + effects[exceptionEffects] = newNodeI(nkArgList, effects.info) + effects[tagEffects] = newNodeI(nkArgList, effects.info) effects[requiresEffects] = g.emptyNode effects[ensuresEffects] = g.emptyNode effects[pragmasEffects] = g.emptyNode +proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) = + rawInitEffects(g, effects) + t.exc = effects[exceptionEffects] t.tags = effects[tagEffects] t.owner = s @@ -1341,10 +1387,14 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = if effects.kind != nkEffectList: return # effects already computed? if not s.hasRealBody: return - if effects.len == effectListLen: return + let emitWarnings = tfEffectSystemWorkaround in s.typ.flags + if effects.len == effectListLen and not emitWarnings: return + + var inferredEffects = newNodeI(nkEffectList, s.info) var t: TEffects - initEffects(g, effects, s, t, c) + initEffects(g, inferredEffects, s, t, c) + rawInitEffects g, effects track(t, body) if s.kind != skMacro: @@ -1369,17 +1419,21 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = let p = s.ast[pragmasPos] let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): - checkRaisesSpec(g, raisesSpec, t.exc, "can raise an unlisted exception: ", + checkRaisesSpec(g, emitWarnings, raisesSpec, t.exc, "can raise an unlisted exception: ", hints=on, subtypeRelation, hintsArg=s.ast[0]) # after the check, use the formal spec: effects[exceptionEffects] = raisesSpec + else: + effects[exceptionEffects] = t.exc let tagsSpec = effectSpec(p, wTags) if not isNil(tagsSpec): - checkRaisesSpec(g, tagsSpec, t.tags, "can have an unlisted effect: ", + checkRaisesSpec(g, emitWarnings, tagsSpec, t.tags, "can have an unlisted effect: ", hints=off, subtypeRelation) # after the check, use the formal spec: effects[tagEffects] = tagsSpec + else: + effects[tagEffects] = t.tags let requiresSpec = propSpec(p, wRequires) if not isNil(requiresSpec): diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 14dc89781..df0e2778b 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1953,6 +1953,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if not hasProto: implicitPragmas(c, s, n.info, validPragmas) + if n[pragmasPos].kind != nkEmpty: + setEffectsForProcType(c.graph, s.typ, n[pragmasPos], s) + s.typ.flags.incl tfEffectSystemWorkaround + # To ease macro generation that produce forwarded .async procs we now # allow a bit redundancy in the pragma declarations. The rule is # a prototype's pragma list must be a superset of the current pragma @@ -2211,8 +2215,9 @@ proc evalInclude(c: PContext, n: PNode): PNode = incMod(c, n, it, result) proc setLine(n: PNode, info: TLineInfo) = - for i in 0..<n.safeLen: setLine(n[i], info) - n.info = info + if n != nil: + for i in 0..<n.safeLen: setLine(n[i], info) + n.info = info proc semPragmaBlock(c: PContext, n: PNode): PNode = checkSonsLen(n, 2, c.config) diff --git a/compiler/types.nim b/compiler/types.nim index f0bd99a17..61f563514 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1364,10 +1364,14 @@ type efTagsDiffer efTagsUnknown efLockLevelsDiffer + efEffectsDelayed proc compatibleEffects*(formal, actual: PType): EffectsCompat = # for proc type compatibility checking: assert formal.kind == tyProc and actual.kind == tyProc + #if tfEffectSystemWorkaround in actual.flags: + # return efCompat + if formal.n[0].kind != nkEffectList or actual.n[0].kind != nkEffectList: return efTagsUnknown @@ -1391,9 +1395,17 @@ proc compatibleEffects*(formal, actual: PType): EffectsCompat = # spec requires some exception or tag, but we don't know anything: if real.len == 0: return efTagsUnknown let res = compatibleEffectsAux(st, real[tagEffects]) - if not res: return efTagsDiffer + if not res: + #if tfEffectSystemWorkaround notin actual.flags: + return efTagsDiffer if formal.lockLevel.ord < 0 or actual.lockLevel.ord <= formal.lockLevel.ord: + + for i in 1 ..< min(formal.n.len, actual.n.len): + if formal.n[i].sym.flags * {sfEffectsDelayed} != actual.n[i].sym.flags * {sfEffectsDelayed}: + result = efEffectsDelayed + break + result = efCompat else: result = efLockLevelsDiffer @@ -1597,7 +1609,8 @@ proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: P msg.add "\n.tag effect is 'any tag allowed'" of efLockLevelsDiffer: msg.add "\nlock levels differ" - + of efEffectsDelayed: + msg.add "\n.effectsOf annotations differ" localError(conf, info, msg) proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool = diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index c3625c5f3..6ff68be75 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -35,7 +35,7 @@ type wMagic = "magic", wThread = "thread", wFinal = "final", wProfiler = "profiler", wMemTracker = "memtracker", wObjChecks = "objchecks", wIntDefine = "intdefine", wStrDefine = "strdefine", wBoolDefine = "booldefine", - wCursor = "cursor", wNoalias = "noalias", + wCursor = "cursor", wNoalias = "noalias", wEffectsOf = "effectsOf", wImmediate = "immediate", wConstructor = "constructor", wDestructor = "destructor", wDelegator = "delegator", wOverride = "override", wImportCpp = "importcpp", |