sempass2.nim 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. import
  10. intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
  11. wordrecg, strutils, options, guards, lineinfos, semfold, semdata,
  12. modulegraphs, lowerings, sigmatch, tables
  13. when not defined(leanCompiler):
  14. import writetracking
  15. when defined(useDfa):
  16. import dfa
  17. import liftdestructors
  18. #[ Second semantic checking pass over the AST. Necessary because the old
  19. way had some inherent problems. Performs:
  20. * effect+exception tracking
  21. * "usage before definition" checking
  22. * also now calls the "lift destructor logic" at strategic positions, this
  23. is about to be put into the spec:
  24. We treat assignment and sinks and destruction as identical.
  25. In the construct let/var x = expr() x's type is marked.
  26. In x = y the type of x is marked.
  27. For every sink parameter of type T T is marked. TODO!
  28. For every call f() the return type of f() is marked.
  29. ]#
  30. # ------------------------ exception and tag tracking -------------------------
  31. discard """
  32. exception tracking:
  33. a() # raises 'x', 'e'
  34. try:
  35. b() # raises 'e'
  36. except e:
  37. # must not undo 'e' here; hrm
  38. c()
  39. --> we need a stack of scopes for this analysis
  40. # XXX enhance the algorithm to care about 'dirty' expressions:
  41. lock a[i].L:
  42. inc i # mark 'i' dirty
  43. lock a[j].L:
  44. access a[i], a[j] # --> reject a[i]
  45. """
  46. type
  47. TEffects = object
  48. exc: PNode # stack of exceptions
  49. tags: PNode # list of tags
  50. bottom, inTryStmt: int
  51. owner: PSym
  52. ownerModule: PSym
  53. init: seq[int] # list of initialized variables
  54. guards: TModel # nested guards
  55. locked: seq[PNode] # locked locations
  56. gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool
  57. inEnforcedNoSideEffects: bool
  58. maxLockLevel, currLockLevel: TLockLevel
  59. config: ConfigRef
  60. graph: ModuleGraph
  61. c: PContext
  62. PEffects = var TEffects
  63. proc `<`(a, b: TLockLevel): bool {.borrow.}
  64. proc `<=`(a, b: TLockLevel): bool {.borrow.}
  65. proc `==`(a, b: TLockLevel): bool {.borrow.}
  66. proc max(a, b: TLockLevel): TLockLevel {.borrow.}
  67. proc isLocalVar(a: PEffects, s: PSym): bool =
  68. s.kind in {skVar, skResult} and sfGlobal notin s.flags and
  69. s.owner == a.owner and s.typ != nil
  70. proc getLockLevel(t: PType): TLockLevel =
  71. var t = t
  72. # tyGenericInst(TLock {tyGenericBody}, tyStatic, tyObject):
  73. if t.kind == tyGenericInst and t.len == 3: t = t.sons[1]
  74. if t.kind == tyStatic and t.n != nil and t.n.kind in {nkCharLit..nkInt64Lit}:
  75. result = t.n.intVal.TLockLevel
  76. proc lockLocations(a: PEffects; pragma: PNode) =
  77. if pragma.kind != nkExprColonExpr:
  78. localError(a.config, pragma.info, "locks pragma without argument")
  79. return
  80. var firstLL = TLockLevel(-1'i16)
  81. for x in pragma[1]:
  82. let thisLL = getLockLevel(x.typ)
  83. if thisLL != 0.TLockLevel:
  84. if thisLL < 0.TLockLevel or thisLL > MaxLockLevel.TLockLevel:
  85. localError(a.config, x.info, "invalid lock level: " & $thisLL)
  86. elif firstLL < 0.TLockLevel: firstLL = thisLL
  87. elif firstLL != thisLL:
  88. localError(a.config, x.info,
  89. "multi-lock requires the same static lock level for every operand")
  90. a.maxLockLevel = max(a.maxLockLevel, firstLL)
  91. a.locked.add x
  92. if firstLL >= 0.TLockLevel and firstLL != a.currLockLevel:
  93. if a.currLockLevel > 0.TLockLevel and a.currLockLevel <= firstLL:
  94. localError(a.config, pragma.info, "invalid nested locking")
  95. a.currLockLevel = firstLL
  96. proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
  97. # check whether the corresponding lock is held:
  98. for L in a.locked:
  99. if L.kind == nkSym and L.sym == guard: return
  100. # we allow accesses nevertheless in top level statements for
  101. # easier initialization:
  102. #if a.isTopLevel:
  103. # message(n.info, warnUnguardedAccess, renderTree(n))
  104. #else:
  105. if not a.isTopLevel:
  106. localError(a.config, n.info, "unguarded access: " & renderTree(n))
  107. # 'guard*' are checks which are concerned with 'guard' annotations
  108. # (var x{.guard: y.}: int)
  109. proc guardDotAccess(a: PEffects; n: PNode) =
  110. let ri = n.sons[1]
  111. if ri.kind != nkSym or ri.sym.kind != skField: return
  112. var g = ri.sym.guard
  113. if g.isNil or a.isTopLevel: return
  114. # fixup guard:
  115. if g.kind == skUnknown:
  116. var field: PSym = nil
  117. var ty = n.sons[0].typ.skipTypes(abstractPtrs)
  118. if ty.kind == tyTuple and not ty.n.isNil:
  119. field = lookupInRecord(ty.n, g.name)
  120. else:
  121. while ty != nil and ty.kind == tyObject:
  122. field = lookupInRecord(ty.n, g.name)
  123. if field != nil: break
  124. ty = ty.sons[0]
  125. if ty == nil: break
  126. ty = ty.skipTypes(skipPtrs)
  127. if field == nil:
  128. localError(a.config, n.info, "invalid guard field: " & g.name.s)
  129. return
  130. g = field
  131. #ri.sym.guard = field
  132. # XXX unfortunately this is not correct for generic instantiations!
  133. if g.kind == skField:
  134. let dot = newNodeI(nkDotExpr, n.info, 2)
  135. dot.sons[0] = n.sons[0]
  136. dot.sons[1] = newSymNode(g)
  137. dot.typ = g.typ
  138. for L in a.locked:
  139. #if a.guards.sameSubexprs(dot, L): return
  140. if guards.sameTree(dot, L): return
  141. localError(a.config, n.info, "unguarded access: " & renderTree(n))
  142. else:
  143. guardGlobal(a, n, g)
  144. proc makeVolatile(a: PEffects; s: PSym) {.inline.} =
  145. template compileToCpp(a): untyped =
  146. a.config.cmd == cmdCompileToCpp or sfCompileToCpp in getModule(a.owner).flags
  147. if a.inTryStmt > 0 and not compileToCpp(a):
  148. incl(s.flags, sfVolatile)
  149. proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
  150. if n.kind != nkSym: return
  151. let s = n.sym
  152. if isLocalVar(a, s):
  153. if volatileCheck: makeVolatile(a, s)
  154. for x in a.init:
  155. if x == s.id: return
  156. a.init.add s.id
  157. proc initVarViaNew(a: PEffects, n: PNode) =
  158. if n.kind != nkSym: return
  159. let s = n.sym
  160. if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
  161. # 'x' is not nil, but that doesn't mean its "not nil" children
  162. # are initialized:
  163. initVar(a, n, volatileCheck=true)
  164. elif isLocalVar(a, s):
  165. makeVolatile(a, s)
  166. proc warnAboutGcUnsafe(n: PNode; conf: ConfigRef) =
  167. #assert false
  168. message(conf, n.info, warnGcUnsafe, renderTree(n))
  169. proc markGcUnsafe(a: PEffects; reason: PSym) =
  170. if not a.inEnforcedGcSafe:
  171. a.gcUnsafe = true
  172. if a.owner.kind in routineKinds: a.owner.gcUnsafetyReason = reason
  173. proc markGcUnsafe(a: PEffects; reason: PNode) =
  174. if not a.inEnforcedGcSafe:
  175. a.gcUnsafe = true
  176. if a.owner.kind in routineKinds:
  177. if reason.kind == nkSym:
  178. a.owner.gcUnsafetyReason = reason.sym
  179. else:
  180. a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name,
  181. a.owner, reason.info, {})
  182. when true:
  183. template markSideEffect(a: PEffects; reason: typed) =
  184. if not a.inEnforcedNoSideEffects: a.hasSideEffect = true
  185. else:
  186. template markSideEffect(a: PEffects; reason: typed) =
  187. if not a.inEnforcedNoSideEffects: a.hasSideEffect = true
  188. markGcUnsafe(a, reason)
  189. proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) =
  190. let u = s.gcUnsafetyReason
  191. if u != nil and not cycleCheck.containsOrIncl(u.id):
  192. let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
  193. case u.kind
  194. of skLet, skVar:
  195. message(conf, s.info, msgKind,
  196. ("'$#' is not GC-safe as it accesses '$#'" &
  197. " which is a global using GC'ed memory") % [s.name.s, u.name.s])
  198. of routineKinds:
  199. # recursive call *always* produces only a warning so the full error
  200. # message is printed:
  201. listGcUnsafety(u, true, cycleCheck, conf)
  202. message(conf, s.info, msgKind,
  203. "'$#' is not GC-safe as it calls '$#'" %
  204. [s.name.s, u.name.s])
  205. of skParam, skForVar:
  206. message(conf, s.info, msgKind,
  207. "'$#' is not GC-safe as it performs an indirect call via '$#'" %
  208. [s.name.s, u.name.s])
  209. else:
  210. message(conf, u.info, msgKind,
  211. "'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
  212. proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) =
  213. var cycleCheck = initIntSet()
  214. listGcUnsafety(s, onlyWarning, cycleCheck, conf)
  215. proc useVar(a: PEffects, n: PNode) =
  216. let s = n.sym
  217. if isLocalVar(a, s):
  218. if sfNoInit in s.flags:
  219. # If the variable is explicitly marked as .noinit. do not emit any error
  220. a.init.add s.id
  221. elif s.id notin a.init:
  222. if {tfNeedsInit, tfNotNil} * s.typ.flags != {}:
  223. message(a.config, n.info, warnProveInit, s.name.s)
  224. else:
  225. message(a.config, n.info, warnUninit, s.name.s)
  226. # prevent superfluous warnings about the same variable:
  227. a.init.add s.id
  228. if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
  229. s.magic != mNimVm:
  230. if s.guard != nil: guardGlobal(a, n, s.guard)
  231. if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
  232. (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
  233. #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
  234. markGcUnsafe(a, s)
  235. markSideEffect(a, s)
  236. else:
  237. markSideEffect(a, s)
  238. type
  239. TIntersection = seq[tuple[id, count: int]] # a simple count table
  240. proc addToIntersection(inter: var TIntersection, s: int) =
  241. for j in 0..<inter.len:
  242. if s == inter[j].id:
  243. inc inter[j].count
  244. return
  245. inter.add((id: s, count: 1))
  246. proc throws(tracked, n: PNode) =
  247. if n.typ == nil or n.typ.kind != tyError: tracked.add n
  248. proc getEbase(g: ModuleGraph; info: TLineInfo): PType =
  249. result = g.sysTypeFromName(info, "Exception")
  250. proc excType(g: ModuleGraph; n: PNode): PType =
  251. # reraise is like raising E_Base:
  252. let t = if n.kind == nkEmpty or n.typ.isNil: getEbase(g, n.info) else: n.typ
  253. result = skipTypes(t, skipPtrs)
  254. proc createRaise(g: ModuleGraph; n: PNode): PNode =
  255. result = newNode(nkType)
  256. result.typ = getEbase(g, n.info)
  257. if not n.isNil: result.info = n.info
  258. proc createTag(g: ModuleGraph; n: PNode): PNode =
  259. result = newNode(nkType)
  260. result.typ = g.sysTypeFromName(n.info, "RootEffect")
  261. if not n.isNil: result.info = n.info
  262. proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
  263. assert e.kind != nkRaiseStmt
  264. var aa = a.exc
  265. for i in a.bottom ..< aa.len:
  266. if sameType(a.graph.excType(aa[i]), a.graph.excType(e)):
  267. if not useLineInfo or a.config.cmd == cmdDoc: return
  268. elif aa[i].info == e.info: return
  269. throws(a.exc, e)
  270. proc addTag(a: PEffects, e: PNode, useLineInfo=true) =
  271. var aa = a.tags
  272. for i in 0 ..< aa.len:
  273. if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)):
  274. if not useLineInfo or a.config.cmd == cmdDoc: return
  275. elif aa[i].info == e.info: return
  276. throws(a.tags, e)
  277. proc mergeEffects(a: PEffects, b, comesFrom: PNode) =
  278. if b.isNil:
  279. addEffect(a, createRaise(a.graph, comesFrom))
  280. else:
  281. for effect in items(b): addEffect(a, effect, useLineInfo=comesFrom != nil)
  282. proc mergeTags(a: PEffects, b, comesFrom: PNode) =
  283. if b.isNil:
  284. addTag(a, createTag(a.graph, comesFrom))
  285. else:
  286. for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil)
  287. proc listEffects(a: PEffects) =
  288. for e in items(a.exc): message(a.config, e.info, hintUser, typeToString(e.typ))
  289. for e in items(a.tags): message(a.config, e.info, hintUser, typeToString(e.typ))
  290. #if a.maxLockLevel != 0:
  291. # message(e.info, hintUser, "lockLevel: " & a.maxLockLevel)
  292. proc catches(tracked: PEffects, e: PType) =
  293. let e = skipTypes(e, skipPtrs)
  294. var L = tracked.exc.len
  295. var i = tracked.bottom
  296. while i < L:
  297. # r supertype of e?
  298. if safeInheritanceDiff(tracked.graph.excType(tracked.exc[i]), e) <= 0:
  299. tracked.exc.sons[i] = tracked.exc.sons[L-1]
  300. dec L
  301. else:
  302. inc i
  303. if tracked.exc.len > 0:
  304. setLen(tracked.exc.sons, L)
  305. else:
  306. assert L == 0
  307. proc catchesAll(tracked: PEffects) =
  308. if tracked.exc.len > 0:
  309. setLen(tracked.exc.sons, tracked.bottom)
  310. proc track(tracked: PEffects, n: PNode)
  311. proc trackTryStmt(tracked: PEffects, n: PNode) =
  312. let oldBottom = tracked.bottom
  313. tracked.bottom = tracked.exc.len
  314. let oldState = tracked.init.len
  315. var inter: TIntersection = @[]
  316. inc tracked.inTryStmt
  317. track(tracked, n.sons[0])
  318. dec tracked.inTryStmt
  319. for i in oldState..<tracked.init.len:
  320. addToIntersection(inter, tracked.init[i])
  321. var branches = 1
  322. var hasFinally = false
  323. # Collect the exceptions caught by the except branches
  324. for i in 1 ..< n.len:
  325. let b = n.sons[i]
  326. let blen = sonsLen(b)
  327. if b.kind == nkExceptBranch:
  328. inc branches
  329. if blen == 1:
  330. catchesAll(tracked)
  331. else:
  332. for j in 0 .. blen - 2:
  333. if b.sons[j].isInfixAs():
  334. assert(b.sons[j][1].kind == nkType)
  335. catches(tracked, b.sons[j][1].typ)
  336. else:
  337. assert(b.sons[j].kind == nkType)
  338. catches(tracked, b.sons[j].typ)
  339. else:
  340. assert b.kind == nkFinally
  341. # Add any other exception raised in the except bodies
  342. for i in 1 ..< n.len:
  343. let b = n.sons[i]
  344. let blen = sonsLen(b)
  345. if b.kind == nkExceptBranch:
  346. setLen(tracked.init, oldState)
  347. track(tracked, b.sons[blen-1])
  348. for i in oldState..<tracked.init.len:
  349. addToIntersection(inter, tracked.init[i])
  350. else:
  351. setLen(tracked.init, oldState)
  352. track(tracked, b.sons[blen-1])
  353. hasFinally = true
  354. tracked.bottom = oldBottom
  355. if not hasFinally:
  356. setLen(tracked.init, oldState)
  357. for id, count in items(inter):
  358. if count == branches: tracked.init.add id
  359. proc isIndirectCall(n: PNode, owner: PSym): bool =
  360. # we don't count f(...) as an indirect call if 'f' is an parameter.
  361. # Instead we track expressions of type tyProc too. See the manual for
  362. # details:
  363. if n.kind != nkSym:
  364. result = true
  365. elif n.sym.kind == skParam:
  366. result = owner != n.sym.owner or owner == nil
  367. elif n.sym.kind notin routineKinds:
  368. result = true
  369. proc isForwardedProc(n: PNode): bool =
  370. result = n.kind == nkSym and sfForward in n.sym.flags
  371. proc trackPragmaStmt(tracked: PEffects, n: PNode) =
  372. for i in 0 ..< sonsLen(n):
  373. var it = n.sons[i]
  374. if whichPragma(it) == wEffects:
  375. # list the computed effects up to here:
  376. listEffects(tracked)
  377. template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
  378. proc importedFromC(n: PNode): bool =
  379. # when imported from C, we assume GC-safety.
  380. result = n.kind == nkSym and sfImportc in n.sym.flags
  381. proc getLockLevel(s: PSym): TLockLevel =
  382. result = s.typ.lockLevel
  383. if result == UnspecifiedLockLevel:
  384. if {sfImportc, sfNoSideEffect} * s.flags != {} or
  385. tfNoSideEffect in s.typ.flags:
  386. result = 0.TLockLevel
  387. else:
  388. result = UnknownLockLevel
  389. #message(s.info, warnUser, "FOR THIS " & s.name.s)
  390. proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) =
  391. if lockLevel >= tracked.currLockLevel:
  392. # if in lock section:
  393. if tracked.currLockLevel > 0.TLockLevel:
  394. localError tracked.config, n.info, errGenerated,
  395. "expected lock level < " & $tracked.currLockLevel &
  396. " but got lock level " & $lockLevel
  397. tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel)
  398. proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
  399. let pragma = s.ast.sons[pragmasPos]
  400. let spec = effectSpec(pragma, wRaises)
  401. mergeEffects(tracked, spec, n)
  402. let tagSpec = effectSpec(pragma, wTags)
  403. mergeTags(tracked, tagSpec, n)
  404. if notGcSafe(s.typ) and sfImportc notin s.flags:
  405. if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
  406. markGcUnsafe(tracked, s)
  407. if tfNoSideEffect notin s.typ.flags:
  408. markSideEffect(tracked, s)
  409. mergeLockLevels(tracked, n, s.getLockLevel)
  410. proc procVarcheck(n: PNode; conf: ConfigRef) =
  411. if n.kind in nkSymChoices:
  412. for x in n: procVarCheck(x, conf)
  413. elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds:
  414. localError(conf, n.info, "'$1' cannot be passed to a procvar" % n.sym.name.s)
  415. proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
  416. let n = n.skipConv
  417. if paramType.isNil or paramType.kind != tyTypeDesc:
  418. procVarcheck skipConvAndClosure(n), tracked.config
  419. #elif n.kind in nkSymChoices:
  420. # echo "came here"
  421. let paramType = paramType.skipTypesOrNil(abstractInst)
  422. if paramType != nil and tfNotNil in paramType.flags and
  423. n.typ != nil and tfNotNil notin n.typ.flags:
  424. if isAddrNode(n):
  425. # addr(x[]) can't be proven, but addr(x) can:
  426. if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
  427. elif (n.kind == nkSym and n.sym.kind in routineKinds) or
  428. (n.kind in procDefs+{nkObjConstr, nkBracket, nkClosure, nkStrLit..nkTripleStrLit}) or
  429. (n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mArrToSeq) or
  430. n.typ.kind == tyTypeDesc:
  431. # 'p' is not nil obviously:
  432. return
  433. case impliesNotNil(tracked.guards, n)
  434. of impUnknown:
  435. message(tracked.config, n.info, errGenerated,
  436. "cannot prove '$1' is not nil" % n.renderTree)
  437. of impNo:
  438. message(tracked.config, n.info, errGenerated,
  439. "'$1' is provably nil" % n.renderTree)
  440. of impYes: discard
  441. proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
  442. addEffect(tracked, createRaise(tracked.graph, n))
  443. addTag(tracked, createTag(tracked.graph, n))
  444. let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
  445. else: op.lockLevel
  446. #if lockLevel == UnknownLockLevel:
  447. # message(n.info, warnUser, "had to assume the worst here")
  448. mergeLockLevels(tracked, n, lockLevel)
  449. proc isOwnedProcVar(n: PNode; owner: PSym): bool =
  450. # XXX prove the soundness of this effect system rule
  451. result = n.kind == nkSym and n.sym.kind == skParam and owner == n.sym.owner
  452. proc isNoEffectList(n: PNode): bool {.inline.} =
  453. assert n.kind == nkEffectList
  454. n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil)
  455. proc isTrival(caller: PNode): bool {.inline.} =
  456. result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved}
  457. proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) =
  458. let a = skipConvAndClosure(n)
  459. let op = a.typ
  460. # assume indirect calls are taken here:
  461. if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and not isTrival(caller):
  462. internalAssert tracked.config, op.n.sons[0].kind == nkEffectList
  463. var effectList = op.n.sons[0]
  464. var s = n.skipConv
  465. if s.kind == nkCast and s[1].typ.kind == tyProc:
  466. s = s[1]
  467. if s.kind == nkSym and s.sym.kind in routineKinds and isNoEffectList(effectList):
  468. propagateEffects(tracked, n, s.sym)
  469. elif isNoEffectList(effectList):
  470. if isForwardedProc(n):
  471. # we have no explicit effects but it's a forward declaration and so it's
  472. # stated there are no additional effects, so simply propagate them:
  473. propagateEffects(tracked, n, n.sym)
  474. elif not isOwnedProcVar(a, tracked.owner):
  475. # we have no explicit effects so assume the worst:
  476. assumeTheWorst(tracked, n, op)
  477. # assume GcUnsafe unless in its type; 'forward' does not matter:
  478. if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
  479. if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
  480. markGcUnsafe(tracked, a)
  481. elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner):
  482. markSideEffect(tracked, a)
  483. else:
  484. mergeEffects(tracked, effectList.sons[exceptionEffects], n)
  485. mergeTags(tracked, effectList.sons[tagEffects], n)
  486. if notGcSafe(op):
  487. if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
  488. markGcUnsafe(tracked, a)
  489. elif tfNoSideEffect notin op.flags:
  490. markSideEffect(tracked, a)
  491. if paramType != nil and paramType.kind == tyVar:
  492. if n.kind == nkSym and isLocalVar(tracked, n.sym):
  493. makeVolatile(tracked, n.sym)
  494. if paramType != nil and paramType.kind == tyProc and tfGcSafe in paramType.flags:
  495. let argtype = skipTypes(a.typ, abstractInst)
  496. # XXX figure out why this can be a non tyProc here. See httpclient.nim for an
  497. # example that triggers it.
  498. if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
  499. localError(tracked.config, n.info, $n & " is not GC safe")
  500. notNilCheck(tracked, n, paramType)
  501. proc breaksBlock(n: PNode): bool =
  502. # sematic check doesn't allow statements after raise, break, return or
  503. # call to noreturn proc, so it is safe to check just the last statements
  504. var it = n
  505. while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
  506. it = it.lastSon
  507. result = it.kind in {nkBreakStmt, nkReturnStmt, nkRaiseStmt} or
  508. it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
  509. proc trackCase(tracked: PEffects, n: PNode) =
  510. track(tracked, n.sons[0])
  511. let oldState = tracked.init.len
  512. let oldFacts = tracked.guards.s.len
  513. let stringCase = skipTypes(n.sons[0].typ,
  514. abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString}
  515. let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and
  516. warnProveField in tracked.config.notes
  517. var inter: TIntersection = @[]
  518. var toCover = 0
  519. for i in 1..<n.len:
  520. let branch = n.sons[i]
  521. setLen(tracked.init, oldState)
  522. if interesting:
  523. setLen(tracked.guards.s, oldFacts)
  524. addCaseBranchFacts(tracked.guards, n, i)
  525. for i in 0 ..< branch.len:
  526. track(tracked, branch.sons[i])
  527. if not breaksBlock(branch.lastSon): inc toCover
  528. for i in oldState..<tracked.init.len:
  529. addToIntersection(inter, tracked.init[i])
  530. setLen(tracked.init, oldState)
  531. if not stringCase or lastSon(n).kind == nkElse:
  532. for id, count in items(inter):
  533. if count >= toCover: tracked.init.add id
  534. # else we can't merge
  535. setLen(tracked.guards.s, oldFacts)
  536. proc trackIf(tracked: PEffects, n: PNode) =
  537. track(tracked, n.sons[0].sons[0])
  538. let oldFacts = tracked.guards.s.len
  539. addFact(tracked.guards, n.sons[0].sons[0])
  540. let oldState = tracked.init.len
  541. var inter: TIntersection = @[]
  542. var toCover = 0
  543. track(tracked, n.sons[0].sons[1])
  544. if not breaksBlock(n.sons[0].sons[1]): inc toCover
  545. for i in oldState..<tracked.init.len:
  546. addToIntersection(inter, tracked.init[i])
  547. for i in 1..<n.len:
  548. let branch = n.sons[i]
  549. setLen(tracked.guards.s, oldFacts)
  550. for j in 0..i-1:
  551. addFactNeg(tracked.guards, n.sons[j].sons[0])
  552. if branch.len > 1:
  553. addFact(tracked.guards, branch.sons[0])
  554. setLen(tracked.init, oldState)
  555. for i in 0 ..< branch.len:
  556. track(tracked, branch.sons[i])
  557. if not breaksBlock(branch.lastSon): inc toCover
  558. for i in oldState..<tracked.init.len:
  559. addToIntersection(inter, tracked.init[i])
  560. setLen(tracked.init, oldState)
  561. if lastSon(n).len == 1:
  562. for id, count in items(inter):
  563. if count >= toCover: tracked.init.add id
  564. # else we can't merge as it is not exhaustive
  565. setLen(tracked.guards.s, oldFacts)
  566. proc trackBlock(tracked: PEffects, n: PNode) =
  567. if n.kind in {nkStmtList, nkStmtListExpr}:
  568. var oldState = -1
  569. for i in 0..<n.len:
  570. if hasSubnodeWith(n.sons[i], nkBreakStmt):
  571. # block:
  572. # x = def
  573. # if ...: ... break # some nested break
  574. # y = def
  575. # --> 'y' not defined after block!
  576. if oldState < 0: oldState = tracked.init.len
  577. track(tracked, n.sons[i])
  578. if oldState > 0: setLen(tracked.init, oldState)
  579. else:
  580. track(tracked, n)
  581. proc isTrue*(n: PNode): bool =
  582. n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
  583. n.kind == nkIntLit and n.intVal != 0
  584. proc paramType(op: PType, i: int): PType =
  585. if op != nil and i < op.len: result = op.sons[i]
  586. proc cstringCheck(tracked: PEffects; n: PNode) =
  587. if n.sons[0].typ.kind == tyCString and (let a = skipConv(n[1]);
  588. a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}):
  589. message(tracked.config, n.info, warnUnsafeCode, renderTree(n))
  590. proc track(tracked: PEffects, n: PNode) =
  591. template gcsafeAndSideeffectCheck() =
  592. if notGcSafe(op) and not importedFromC(a):
  593. # and it's not a recursive call:
  594. if not (a.kind == nkSym and a.sym == tracked.owner):
  595. if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
  596. markGcUnsafe(tracked, a)
  597. if tfNoSideEffect notin op.flags and not importedFromC(a):
  598. # and it's not a recursive call:
  599. if not (a.kind == nkSym and a.sym == tracked.owner):
  600. markSideEffect(tracked, a)
  601. case n.kind
  602. of nkSym:
  603. useVar(tracked, n)
  604. of nkRaiseStmt:
  605. if n[0].kind != nkEmpty:
  606. n.sons[0].info = n.info
  607. #throws(tracked.exc, n.sons[0])
  608. addEffect(tracked, n.sons[0], useLineInfo=false)
  609. for i in 0 ..< safeLen(n):
  610. track(tracked, n.sons[i])
  611. else:
  612. # A `raise` with no arguments means we're going to re-raise the exception
  613. # being handled or, if outside of an `except` block, a `ReraiseError`.
  614. # Here we add a `Exception` tag in order to cover both the cases.
  615. addEffect(tracked, createRaise(tracked.graph, n))
  616. of nkCallKinds:
  617. # p's effects are ours too:
  618. var a = n.sons[0]
  619. let op = a.typ
  620. if getConstExpr(tracked.ownerModule, n, tracked.graph) != nil:
  621. return
  622. if n.typ != nil:
  623. if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
  624. createTypeBoundOps(tracked.graph, tracked.c, n.typ, n.info)
  625. if a.kind == nkCast and a[1].typ.kind == tyProc:
  626. a = a[1]
  627. # XXX: in rare situations, templates and macros will reach here after
  628. # calling getAst(templateOrMacro()). Currently, templates and macros
  629. # are indistinguishable from normal procs (both have tyProc type) and
  630. # we can detect them only by checking for attached nkEffectList.
  631. if op != nil and op.kind == tyProc and op.n.sons[0].kind == nkEffectList:
  632. if a.kind == nkSym:
  633. if a.sym == tracked.owner: tracked.isRecursive = true
  634. # even for recursive calls we need to check the lock levels (!):
  635. mergeLockLevels(tracked, n, a.sym.getLockLevel)
  636. if sfSideEffect in a.sym.flags: markSideEffect(tracked, a)
  637. else:
  638. mergeLockLevels(tracked, n, op.lockLevel)
  639. var effectList = op.n.sons[0]
  640. if a.kind == nkSym and a.sym.kind == skMethod:
  641. propagateEffects(tracked, n, a.sym)
  642. elif isNoEffectList(effectList):
  643. if isForwardedProc(a):
  644. propagateEffects(tracked, n, a.sym)
  645. elif isIndirectCall(a, tracked.owner):
  646. assumeTheWorst(tracked, n, op)
  647. gcsafeAndSideeffectCheck()
  648. else:
  649. mergeEffects(tracked, effectList.sons[exceptionEffects], n)
  650. mergeTags(tracked, effectList.sons[tagEffects], n)
  651. gcsafeAndSideeffectCheck()
  652. if a.kind != nkSym or a.sym.magic != mNBindSym:
  653. for i in 1 ..< n.len: trackOperand(tracked, n.sons[i], paramType(op, i), a)
  654. if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
  655. # may not look like an assignment, but it is:
  656. let arg = n.sons[1]
  657. initVarViaNew(tracked, arg)
  658. if arg.typ.len != 0 and {tfNeedsInit} * arg.typ.lastSon.flags != {}:
  659. if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
  660. n[2].intVal == 0:
  661. # var s: seq[notnil]; newSeq(s, 0) is a special case!
  662. discard
  663. else:
  664. message(tracked.config, arg.info, warnProveInit, $arg)
  665. for i in 0 ..< safeLen(n):
  666. track(tracked, n.sons[i])
  667. of nkDotExpr:
  668. guardDotAccess(tracked, n)
  669. for i in 0 ..< n.len: track(tracked, n.sons[i])
  670. of nkCheckedFieldExpr:
  671. track(tracked, n.sons[0])
  672. if warnProveField in tracked.config.notes:
  673. checkFieldAccess(tracked.guards, n, tracked.config)
  674. of nkTryStmt: trackTryStmt(tracked, n)
  675. of nkPragma: trackPragmaStmt(tracked, n)
  676. of nkAsgn, nkFastAsgn:
  677. track(tracked, n.sons[1])
  678. initVar(tracked, n.sons[0], volatileCheck=true)
  679. invalidateFacts(tracked.guards, n.sons[0])
  680. track(tracked, n.sons[0])
  681. addAsgnFact(tracked.guards, n.sons[0], n.sons[1])
  682. notNilCheck(tracked, n.sons[1], n.sons[0].typ)
  683. when false: cstringCheck(tracked, n)
  684. if tracked.owner.kind != skMacro:
  685. createTypeBoundOps(tracked.graph, tracked.c, n[0].typ, n.info)
  686. of nkVarSection, nkLetSection:
  687. for child in n:
  688. let last = lastSon(child)
  689. if last.kind != nkEmpty: track(tracked, last)
  690. if tracked.owner.kind != skMacro:
  691. if child.kind == nkVarTuple:
  692. createTypeBoundOps(tracked.graph, tracked.c, child[^1].typ, child.info)
  693. for i in 0..child.len-3:
  694. createTypeBoundOps(tracked.graph, tracked.c, child[i].typ, child.info)
  695. else:
  696. createTypeBoundOps(tracked.graph, tracked.c, child[0].typ, child.info)
  697. if child.kind == nkIdentDefs and last.kind != nkEmpty:
  698. for i in 0 .. child.len-3:
  699. initVar(tracked, child.sons[i], volatileCheck=false)
  700. addAsgnFact(tracked.guards, child.sons[i], last)
  701. notNilCheck(tracked, last, child.sons[i].typ)
  702. elif child.kind == nkVarTuple and last.kind != nkEmpty:
  703. for i in 0 .. child.len-2:
  704. if child[i].kind == nkEmpty or
  705. child[i].kind == nkSym and child[i].sym.name.s == "_":
  706. continue
  707. initVar(tracked, child[i], volatileCheck=false)
  708. if last.kind in {nkPar, nkTupleConstr}:
  709. addAsgnFact(tracked.guards, child[i], last[i])
  710. notNilCheck(tracked, last[i], child[i].typ)
  711. # since 'var (a, b): T = ()' is not even allowed, there is always type
  712. # inference for (a, b) and thus no nil checking is necessary.
  713. of nkConstSection:
  714. for child in n:
  715. let last = lastSon(child)
  716. track(tracked, last)
  717. of nkCaseStmt: trackCase(tracked, n)
  718. of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
  719. of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n.sons[1])
  720. of nkWhileStmt:
  721. track(tracked, n.sons[0])
  722. # 'while true' loop?
  723. if isTrue(n.sons[0]):
  724. trackBlock(tracked, n.sons[1])
  725. else:
  726. # loop may never execute:
  727. let oldState = tracked.init.len
  728. let oldFacts = tracked.guards.s.len
  729. addFact(tracked.guards, n.sons[0])
  730. track(tracked, n.sons[1])
  731. setLen(tracked.init, oldState)
  732. setLen(tracked.guards.s, oldFacts)
  733. of nkForStmt, nkParForStmt:
  734. # we are very conservative here and assume the loop is never executed:
  735. let oldState = tracked.init.len
  736. for i in 0 .. n.len-3:
  737. let it = n[i]
  738. track(tracked, it)
  739. if tracked.owner.kind != skMacro:
  740. if it.kind == nkVarTuple:
  741. for x in it:
  742. createTypeBoundOps(tracked.graph, tracked.c, x.typ, x.info)
  743. else:
  744. createTypeBoundOps(tracked.graph, tracked.c, it.typ, it.info)
  745. let iterCall = n[n.len-2]
  746. let loopBody = n[n.len-1]
  747. if tracked.owner.kind != skMacro and iterCall.safelen > 1:
  748. # XXX this is a bit hacky:
  749. if iterCall[1].typ != nil and iterCall[1].typ.skipTypes(abstractVar).kind notin {tyVarargs, tyOpenArray}:
  750. createTypeBoundOps(tracked.graph, tracked.c, iterCall[1].typ, iterCall[1].info)
  751. track(tracked, iterCall)
  752. track(tracked, loopBody)
  753. setLen(tracked.init, oldState)
  754. of nkObjConstr:
  755. when false: track(tracked, n.sons[0])
  756. let oldFacts = tracked.guards.s.len
  757. for i in 1 ..< n.len:
  758. let x = n.sons[i]
  759. track(tracked, x)
  760. if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
  761. addDiscriminantFact(tracked.guards, x)
  762. setLen(tracked.guards.s, oldFacts)
  763. of nkPragmaBlock:
  764. let pragmaList = n.sons[0]
  765. let oldLocked = tracked.locked.len
  766. let oldLockLevel = tracked.currLockLevel
  767. var enforcedGcSafety = false
  768. var enforceNoSideEffects = false
  769. for i in 0 ..< pragmaList.len:
  770. let pragma = whichPragma(pragmaList.sons[i])
  771. if pragma == wLocks:
  772. lockLocations(tracked, pragmaList.sons[i])
  773. elif pragma == wGcSafe:
  774. enforcedGcSafety = true
  775. elif pragma == wNosideeffect:
  776. enforceNoSideEffects = true
  777. if enforcedGcSafety: tracked.inEnforcedGcSafe = true
  778. if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
  779. track(tracked, n.lastSon)
  780. if enforcedGcSafety: tracked.inEnforcedGcSafe = false
  781. if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false
  782. setLen(tracked.locked, oldLocked)
  783. tracked.currLockLevel = oldLockLevel
  784. of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
  785. nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
  786. discard
  787. of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv:
  788. if n.len == 2: track(tracked, n.sons[1])
  789. of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
  790. if n.len == 1: track(tracked, n.sons[0])
  791. else:
  792. for i in 0 ..< safeLen(n): track(tracked, n.sons[i])
  793. proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
  794. result = safeInheritanceDiff(g.excType(real), spec.typ) <= 0
  795. proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool;
  796. effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.}) =
  797. # check that any real exception is listed in 'spec'; mark those as used;
  798. # report any unused exception
  799. var used = initIntSet()
  800. for r in items(real):
  801. block search:
  802. for s in 0 ..< spec.len:
  803. if effectPredicate(g, spec[s], r):
  804. used.incl(s)
  805. break search
  806. # XXX call graph analysis would be nice here!
  807. pushInfoContext(g.config, spec.info)
  808. localError(g.config, r.info, errGenerated, msg & typeToString(r.typ))
  809. popInfoContext(g.config)
  810. # hint about unnecessarily listed exception types:
  811. if hints:
  812. for s in 0 ..< spec.len:
  813. if not used.contains(s):
  814. message(g.config, spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s]))
  815. proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) =
  816. ## checks for consistent effects for multi methods.
  817. let actual = branch.typ.n.sons[0]
  818. if actual.len != effectListLen: return
  819. let p = disp.ast.sons[pragmasPos]
  820. let raisesSpec = effectSpec(p, wRaises)
  821. if not isNil(raisesSpec):
  822. checkRaisesSpec(g, raisesSpec, actual.sons[exceptionEffects],
  823. "can raise an unlisted exception: ", hints=off, subtypeRelation)
  824. let tagsSpec = effectSpec(p, wTags)
  825. if not isNil(tagsSpec):
  826. checkRaisesSpec(g, tagsSpec, actual.sons[tagEffects],
  827. "can have an unlisted effect: ", hints=off, subtypeRelation)
  828. if sfThread in disp.flags and notGcSafe(branch.typ):
  829. localError(g.config, branch.info, "base method is GC-safe, but '$1' is not" %
  830. branch.name.s)
  831. if branch.typ.lockLevel > disp.typ.lockLevel:
  832. when true:
  833. message(g.config, branch.info, warnLockLevel,
  834. "base method has lock level $1, but dispatcher has $2" %
  835. [$branch.typ.lockLevel, $disp.typ.lockLevel])
  836. else:
  837. # XXX make this an error after bigbreak has been released:
  838. localError(g.config, branch.info,
  839. "base method has lock level $1, but dispatcher has $2" %
  840. [$branch.typ.lockLevel, $disp.typ.lockLevel])
  841. proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) =
  842. var effects = t.n[0]
  843. if t.kind != tyProc or effects.kind != nkEffectList: return
  844. if n.kind != nkEmpty:
  845. internalAssert g.config, effects.len == 0
  846. newSeq(effects.sons, effectListLen)
  847. let raisesSpec = effectSpec(n, wRaises)
  848. if not isNil(raisesSpec):
  849. effects[exceptionEffects] = raisesSpec
  850. let tagsSpec = effectSpec(n, wTags)
  851. if not isNil(tagsSpec):
  852. effects[tagEffects] = tagsSpec
  853. effects[pragmasEffects] = n
  854. proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) =
  855. newSeq(effects.sons, effectListLen)
  856. effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
  857. effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
  858. effects.sons[usesEffects] = g.emptyNode
  859. effects.sons[writeEffects] = g.emptyNode
  860. effects.sons[pragmasEffects] = g.emptyNode
  861. t.exc = effects.sons[exceptionEffects]
  862. t.tags = effects.sons[tagEffects]
  863. t.owner = s
  864. t.ownerModule = s.getModule
  865. t.init = @[]
  866. t.guards.s = @[]
  867. t.guards.o = initOperators(g)
  868. t.locked = @[]
  869. t.graph = g
  870. t.config = g.config
  871. t.c = c
  872. proc trackProc*(c: PContext; s: PSym, body: PNode) =
  873. let g = c.graph
  874. var effects = s.typ.n.sons[0]
  875. if effects.kind != nkEffectList: return
  876. # effects already computed?
  877. if sfForward in s.flags: return
  878. if effects.len == effectListLen: return
  879. var t: TEffects
  880. initEffects(g, effects, s, t, c)
  881. track(t, body)
  882. if not isEmptyType(s.typ.sons[0]) and
  883. ({tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} or
  884. s.typ.sons[0].skipTypes(abstractInst).kind == tyVar) and
  885. s.kind in {skProc, skFunc, skConverter, skMethod}:
  886. var res = s.ast.sons[resultPos].sym # get result symbol
  887. if res.id notin t.init:
  888. message(g.config, body.info, warnProveInit, "result")
  889. let p = s.ast.sons[pragmasPos]
  890. let raisesSpec = effectSpec(p, wRaises)
  891. if not isNil(raisesSpec):
  892. checkRaisesSpec(g, raisesSpec, t.exc, "can raise an unlisted exception: ",
  893. hints=on, subtypeRelation)
  894. # after the check, use the formal spec:
  895. effects.sons[exceptionEffects] = raisesSpec
  896. let tagsSpec = effectSpec(p, wTags)
  897. if not isNil(tagsSpec):
  898. checkRaisesSpec(g, tagsSpec, t.tags, "can have an unlisted effect: ",
  899. hints=off, subtypeRelation)
  900. # after the check, use the formal spec:
  901. effects.sons[tagEffects] = tagsSpec
  902. if sfThread in s.flags and t.gcUnsafe:
  903. if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions:
  904. #localError(s.info, "'$1' is not GC-safe" % s.name.s)
  905. listGcUnsafety(s, onlyWarning=false, g.config)
  906. else:
  907. listGcUnsafety(s, onlyWarning=true, g.config)
  908. #localError(s.info, warnGcUnsafe2, s.name.s)
  909. if sfNoSideEffect in s.flags and t.hasSideEffect:
  910. when false:
  911. listGcUnsafety(s, onlyWarning=false, g.config)
  912. else:
  913. localError(g.config, s.info, "'$1' can have side effects" % s.name.s)
  914. if not t.gcUnsafe:
  915. s.typ.flags.incl tfGcSafe
  916. if not t.hasSideEffect and sfSideEffect notin s.flags:
  917. s.typ.flags.incl tfNoSideEffect
  918. if s.typ.lockLevel == UnspecifiedLockLevel:
  919. s.typ.lockLevel = t.maxLockLevel
  920. elif t.maxLockLevel > s.typ.lockLevel:
  921. #localError(s.info,
  922. message(g.config, s.info, warnLockLevel,
  923. "declared lock level is $1, but real lock level is $2" %
  924. [$s.typ.lockLevel, $t.maxLockLevel])
  925. when defined(useDfa):
  926. if s.name.s == "testp":
  927. dataflowAnalysis(s, body)
  928. when false: trackWrites(s, body)
  929. proc trackTopLevelStmt*(c: PContext; module: PSym; n: PNode) =
  930. if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
  931. nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
  932. return
  933. let g = c.graph
  934. var effects = newNode(nkEffectList, n.info)
  935. var t: TEffects
  936. initEffects(g, effects, module, t, c)
  937. t.isToplevel = true
  938. track(t, n)