sempass2.nim 39 KB

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