sempass2.nim 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029
  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 s.id notin a.init:
  210. if {tfNeedsInit, tfNotNil} * s.typ.flags != {}:
  211. message(a.config, n.info, warnProveInit, s.name.s)
  212. else:
  213. message(a.config, n.info, warnUninit, s.name.s)
  214. # prevent superfluous warnings about the same variable:
  215. a.init.add s.id
  216. if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
  217. s.magic != mNimVm:
  218. if s.guard != nil: guardGlobal(a, n, s.guard)
  219. if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
  220. (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
  221. #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
  222. markGcUnsafe(a, s)
  223. markSideEffect(a, s)
  224. else:
  225. markSideEffect(a, s)
  226. type
  227. TIntersection = seq[tuple[id, count: int]] # a simple count table
  228. proc addToIntersection(inter: var TIntersection, s: int) =
  229. for j in 0..<inter.len:
  230. if s == inter[j].id:
  231. inc inter[j].count
  232. return
  233. inter.add((id: s, count: 1))
  234. proc throws(tracked, n: PNode) =
  235. if n.typ == nil or n.typ.kind != tyError: tracked.add n
  236. proc getEbase(g: ModuleGraph; info: TLineInfo): PType =
  237. result = g.sysTypeFromName(info, "Exception")
  238. proc excType(g: ModuleGraph; n: PNode): PType =
  239. # reraise is like raising E_Base:
  240. let t = if n.kind == nkEmpty or n.typ.isNil: getEbase(g, n.info) else: n.typ
  241. result = skipTypes(t, skipPtrs)
  242. proc createRaise(g: ModuleGraph; n: PNode): PNode =
  243. result = newNode(nkType)
  244. result.typ = getEbase(g, n.info)
  245. if not n.isNil: result.info = n.info
  246. proc createTag(g: ModuleGraph; n: PNode): PNode =
  247. result = newNode(nkType)
  248. result.typ = g.sysTypeFromName(n.info, "RootEffect")
  249. if not n.isNil: result.info = n.info
  250. proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
  251. assert e.kind != nkRaiseStmt
  252. var aa = a.exc
  253. for i in a.bottom ..< aa.len:
  254. if sameType(a.graph.excType(aa[i]), a.graph.excType(e)):
  255. if not useLineInfo or a.config.cmd == cmdDoc: return
  256. elif aa[i].info == e.info: return
  257. throws(a.exc, e)
  258. proc addTag(a: PEffects, e: PNode, useLineInfo=true) =
  259. var aa = a.tags
  260. for i in 0 ..< aa.len:
  261. if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)):
  262. if not useLineInfo or a.config.cmd == cmdDoc: return
  263. elif aa[i].info == e.info: return
  264. throws(a.tags, e)
  265. proc mergeEffects(a: PEffects, b, comesFrom: PNode) =
  266. if b.isNil:
  267. addEffect(a, createRaise(a.graph, comesFrom))
  268. else:
  269. for effect in items(b): addEffect(a, effect, useLineInfo=comesFrom != nil)
  270. proc mergeTags(a: PEffects, b, comesFrom: PNode) =
  271. if b.isNil:
  272. addTag(a, createTag(a.graph, comesFrom))
  273. else:
  274. for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil)
  275. proc listEffects(a: PEffects) =
  276. for e in items(a.exc): message(a.config, e.info, hintUser, typeToString(e.typ))
  277. for e in items(a.tags): message(a.config, e.info, hintUser, typeToString(e.typ))
  278. #if a.maxLockLevel != 0:
  279. # message(e.info, hintUser, "lockLevel: " & a.maxLockLevel)
  280. proc catches(tracked: PEffects, e: PType) =
  281. let e = skipTypes(e, skipPtrs)
  282. var L = tracked.exc.len
  283. var i = tracked.bottom
  284. while i < L:
  285. # r supertype of e?
  286. if safeInheritanceDiff(tracked.graph.excType(tracked.exc[i]), e) <= 0:
  287. tracked.exc.sons[i] = tracked.exc.sons[L-1]
  288. dec L
  289. else:
  290. inc i
  291. if tracked.exc.len > 0:
  292. setLen(tracked.exc.sons, L)
  293. else:
  294. assert L == 0
  295. proc catchesAll(tracked: PEffects) =
  296. if tracked.exc.len > 0:
  297. setLen(tracked.exc.sons, tracked.bottom)
  298. proc track(tracked: PEffects, n: PNode)
  299. proc trackTryStmt(tracked: PEffects, n: PNode) =
  300. let oldBottom = tracked.bottom
  301. tracked.bottom = tracked.exc.len
  302. let oldState = tracked.init.len
  303. var inter: TIntersection = @[]
  304. inc tracked.inTryStmt
  305. track(tracked, n.sons[0])
  306. dec tracked.inTryStmt
  307. for i in oldState..<tracked.init.len:
  308. addToIntersection(inter, tracked.init[i])
  309. var branches = 1
  310. var hasFinally = false
  311. for i in 1 ..< n.len:
  312. let b = n.sons[i]
  313. let blen = sonsLen(b)
  314. if b.kind == nkExceptBranch:
  315. inc branches
  316. if blen == 1:
  317. catchesAll(tracked)
  318. else:
  319. for j in countup(0, blen - 2):
  320. if b.sons[j].isInfixAs():
  321. assert(b.sons[j][1].kind == nkType)
  322. catches(tracked, b.sons[j][1].typ)
  323. else:
  324. assert(b.sons[j].kind == nkType)
  325. catches(tracked, b.sons[j].typ)
  326. setLen(tracked.init, oldState)
  327. track(tracked, b.sons[blen-1])
  328. for i in oldState..<tracked.init.len:
  329. addToIntersection(inter, tracked.init[i])
  330. else:
  331. assert b.kind == nkFinally
  332. setLen(tracked.init, oldState)
  333. track(tracked, b.sons[blen-1])
  334. hasFinally = true
  335. tracked.bottom = oldBottom
  336. if not hasFinally:
  337. setLen(tracked.init, oldState)
  338. for id, count in items(inter):
  339. if count == branches: tracked.init.add id
  340. proc isIndirectCall(n: PNode, owner: PSym): bool =
  341. # we don't count f(...) as an indirect call if 'f' is an parameter.
  342. # Instead we track expressions of type tyProc too. See the manual for
  343. # details:
  344. if n.kind != nkSym:
  345. result = true
  346. elif n.sym.kind == skParam:
  347. result = owner != n.sym.owner or owner == nil
  348. elif n.sym.kind notin routineKinds:
  349. result = true
  350. proc isForwardedProc(n: PNode): bool =
  351. result = n.kind == nkSym and sfForward in n.sym.flags
  352. proc trackPragmaStmt(tracked: PEffects, n: PNode) =
  353. for i in countup(0, sonsLen(n) - 1):
  354. var it = n.sons[i]
  355. if whichPragma(it) == wEffects:
  356. # list the computed effects up to here:
  357. listEffects(tracked)
  358. proc effectSpec(n: PNode, effectType: TSpecialWord): PNode =
  359. for i in countup(0, sonsLen(n) - 1):
  360. var it = n.sons[i]
  361. if it.kind == nkExprColonExpr and whichPragma(it) == effectType:
  362. result = it.sons[1]
  363. if result.kind notin {nkCurly, nkBracket}:
  364. result = newNodeI(nkCurly, result.info)
  365. result.add(it.sons[1])
  366. return
  367. proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
  368. let spec = effectSpec(x, effectType)
  369. if isNil(spec):
  370. let s = n.sons[namePos].sym
  371. let actual = s.typ.n.sons[0]
  372. if actual.len != effectListLen: return
  373. let real = actual.sons[idx]
  374. # warning: hack ahead:
  375. var effects = newNodeI(nkBracket, n.info, real.len)
  376. for i in 0 ..< real.len:
  377. var t = typeToString(real[i].typ)
  378. if t.startsWith("ref "): t = substr(t, 4)
  379. effects.sons[i] = newIdentNode(getIdent(cache, t), n.info)
  380. # set the type so that the following analysis doesn't screw up:
  381. effects.sons[i].typ = real[i].typ
  382. result = newNode(nkExprColonExpr, n.info, @[
  383. newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects])
  384. proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
  385. let s = n.sons[namePos].sym
  386. let params = s.typ.n
  387. var effects = newNodeI(nkBracket, n.info)
  388. for i in 1 ..< params.len:
  389. if params[i].kind == nkSym and flag in params[i].sym.flags:
  390. effects.add params[i]
  391. if effects.len > 0:
  392. result = newNode(nkExprColonExpr, n.info, @[
  393. newIdentNode(getIdent(cache, pragmaName), n.info), effects])
  394. proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
  395. let s = n.sons[namePos].sym
  396. if tfReturnsNew in s.typ.flags:
  397. result = newIdentNode(getIdent(cache, "new"), n.info)
  398. proc documentRaises*(cache: IdentCache; n: PNode) =
  399. if n.sons[namePos].kind != nkSym: return
  400. let pragmas = n.sons[pragmasPos]
  401. let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects)
  402. let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects)
  403. let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes")
  404. let p4 = documentNewEffect(cache, n)
  405. let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes")
  406. if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil:
  407. if pragmas.kind == nkEmpty:
  408. n.sons[pragmasPos] = newNodeI(nkPragma, n.info)
  409. if p1 != nil: n.sons[pragmasPos].add p1
  410. if p2 != nil: n.sons[pragmasPos].add p2
  411. if p3 != nil: n.sons[pragmasPos].add p3
  412. if p4 != nil: n.sons[pragmasPos].add p4
  413. if p5 != nil: n.sons[pragmasPos].add p5
  414. template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
  415. proc importedFromC(n: PNode): bool =
  416. # when imported from C, we assume GC-safety.
  417. result = n.kind == nkSym and sfImportc in n.sym.flags
  418. proc getLockLevel(s: PSym): TLockLevel =
  419. result = s.typ.lockLevel
  420. if result == UnspecifiedLockLevel:
  421. if {sfImportc, sfNoSideEffect} * s.flags != {} or
  422. tfNoSideEffect in s.typ.flags:
  423. result = 0.TLockLevel
  424. else:
  425. result = UnknownLockLevel
  426. #message(s.info, warnUser, "FOR THIS " & s.name.s)
  427. proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) =
  428. if lockLevel >= tracked.currLockLevel:
  429. # if in lock section:
  430. if tracked.currLockLevel > 0.TLockLevel:
  431. localError tracked.config, n.info, errGenerated,
  432. "expected lock level < " & $tracked.currLockLevel &
  433. " but got lock level " & $lockLevel
  434. tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel)
  435. proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
  436. let pragma = s.ast.sons[pragmasPos]
  437. let spec = effectSpec(pragma, wRaises)
  438. mergeEffects(tracked, spec, n)
  439. let tagSpec = effectSpec(pragma, wTags)
  440. mergeTags(tracked, tagSpec, n)
  441. if notGcSafe(s.typ) and sfImportc notin s.flags:
  442. if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
  443. markGcUnsafe(tracked, s)
  444. if tfNoSideEffect notin s.typ.flags:
  445. markSideEffect(tracked, s)
  446. mergeLockLevels(tracked, n, s.getLockLevel)
  447. proc procVarcheck(n: PNode; conf: ConfigRef) =
  448. if n.kind in nkSymChoices:
  449. for x in n: procVarCheck(x, conf)
  450. elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds:
  451. localError(conf, n.info, "'$1' cannot be passed to a procvar" % n.sym.name.s)
  452. proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
  453. let n = n.skipConv
  454. if paramType.isNil or paramType.kind != tyTypeDesc:
  455. procVarcheck skipConvAndClosure(n), tracked.config
  456. #elif n.kind in nkSymChoices:
  457. # echo "came here"
  458. let paramType = paramType.skipTypesOrNil(abstractInst)
  459. if paramType != nil and tfNotNil in paramType.flags and
  460. n.typ != nil and tfNotNil notin n.typ.flags:
  461. if n.kind == nkAddr:
  462. # addr(x[]) can't be proven, but addr(x) can:
  463. if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
  464. elif (n.kind == nkSym and n.sym.kind in routineKinds) or
  465. (n.kind in procDefs+{nkObjConstr, nkBracket, nkClosure, nkStrLit..nkTripleStrLit}) or
  466. (n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mArrToSeq):
  467. # 'p' is not nil obviously:
  468. return
  469. case impliesNotNil(tracked.guards, n)
  470. of impUnknown:
  471. message(tracked.config, n.info, errGenerated,
  472. "cannot prove '$1' is not nil" % n.renderTree)
  473. of impNo:
  474. message(tracked.config, n.info, errGenerated,
  475. "'$1' is provably nil" % n.renderTree)
  476. of impYes: discard
  477. proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
  478. addEffect(tracked, createRaise(tracked.graph, n))
  479. addTag(tracked, createTag(tracked.graph, n))
  480. let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
  481. else: op.lockLevel
  482. #if lockLevel == UnknownLockLevel:
  483. # message(n.info, warnUser, "had to assume the worst here")
  484. mergeLockLevels(tracked, n, lockLevel)
  485. proc isOwnedProcVar(n: PNode; owner: PSym): bool =
  486. # XXX prove the soundness of this effect system rule
  487. result = n.kind == nkSym and n.sym.kind == skParam and owner == n.sym.owner
  488. proc isNoEffectList(n: PNode): bool {.inline.} =
  489. assert n.kind == nkEffectList
  490. n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil)
  491. proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
  492. let a = skipConvAndClosure(n)
  493. let op = a.typ
  494. if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit:
  495. internalAssert tracked.config, op.n.sons[0].kind == nkEffectList
  496. var effectList = op.n.sons[0]
  497. var s = n.skipConv
  498. if s.kind == nkCast and s[1].typ.kind == tyProc:
  499. s = s[1]
  500. if s.kind == nkSym and s.sym.kind in routineKinds and isNoEffectList(effectList):
  501. propagateEffects(tracked, n, s.sym)
  502. elif isNoEffectList(effectList):
  503. if isForwardedProc(n):
  504. # we have no explicit effects but it's a forward declaration and so it's
  505. # stated there are no additional effects, so simply propagate them:
  506. propagateEffects(tracked, n, n.sym)
  507. elif not isOwnedProcVar(a, tracked.owner):
  508. # we have no explicit effects so assume the worst:
  509. assumeTheWorst(tracked, n, op)
  510. # assume GcUnsafe unless in its type; 'forward' does not matter:
  511. if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
  512. if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
  513. markGcUnsafe(tracked, a)
  514. elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner):
  515. markSideEffect(tracked, a)
  516. else:
  517. mergeEffects(tracked, effectList.sons[exceptionEffects], n)
  518. mergeTags(tracked, effectList.sons[tagEffects], n)
  519. if notGcSafe(op):
  520. if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
  521. markGcUnsafe(tracked, a)
  522. elif tfNoSideEffect notin op.flags:
  523. markSideEffect(tracked, a)
  524. if paramType != nil and paramType.kind == tyVar:
  525. if n.kind == nkSym and isLocalVar(tracked, n.sym):
  526. makeVolatile(tracked, n.sym)
  527. if paramType != nil and paramType.kind == tyProc and tfGcSafe in paramType.flags:
  528. let argtype = skipTypes(a.typ, abstractInst)
  529. # XXX figure out why this can be a non tyProc here. See httpclient.nim for an
  530. # example that triggers it.
  531. if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
  532. localError(tracked.config, n.info, $n & " is not GC safe")
  533. notNilCheck(tracked, n, paramType)
  534. proc breaksBlock(n: PNode): bool =
  535. # sematic check doesn't allow statements after raise, break, return or
  536. # call to noreturn proc, so it is safe to check just the last statements
  537. var it = n
  538. while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
  539. it = it.lastSon
  540. result = it.kind in {nkBreakStmt, nkReturnStmt, nkRaiseStmt} or
  541. it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
  542. proc trackCase(tracked: PEffects, n: PNode) =
  543. track(tracked, n.sons[0])
  544. let oldState = tracked.init.len
  545. let oldFacts = tracked.guards.s.len
  546. let stringCase = skipTypes(n.sons[0].typ,
  547. abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString}
  548. let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and
  549. warnProveField in tracked.config.notes
  550. var inter: TIntersection = @[]
  551. var toCover = 0
  552. for i in 1..<n.len:
  553. let branch = n.sons[i]
  554. setLen(tracked.init, oldState)
  555. if interesting:
  556. setLen(tracked.guards.s, oldFacts)
  557. addCaseBranchFacts(tracked.guards, n, i)
  558. for i in 0 ..< branch.len:
  559. track(tracked, branch.sons[i])
  560. if not breaksBlock(branch.lastSon): inc toCover
  561. for i in oldState..<tracked.init.len:
  562. addToIntersection(inter, tracked.init[i])
  563. setLen(tracked.init, oldState)
  564. if not stringCase or lastSon(n).kind == nkElse:
  565. for id, count in items(inter):
  566. if count >= toCover: tracked.init.add id
  567. # else we can't merge
  568. setLen(tracked.guards.s, oldFacts)
  569. proc trackIf(tracked: PEffects, n: PNode) =
  570. track(tracked, n.sons[0].sons[0])
  571. let oldFacts = tracked.guards.s.len
  572. addFact(tracked.guards, n.sons[0].sons[0])
  573. let oldState = tracked.init.len
  574. var inter: TIntersection = @[]
  575. var toCover = 0
  576. track(tracked, n.sons[0].sons[1])
  577. if not breaksBlock(n.sons[0].sons[1]): inc toCover
  578. for i in oldState..<tracked.init.len:
  579. addToIntersection(inter, tracked.init[i])
  580. for i in 1..<n.len:
  581. let branch = n.sons[i]
  582. setLen(tracked.guards.s, oldFacts)
  583. for j in 0..i-1:
  584. addFactNeg(tracked.guards, n.sons[j].sons[0])
  585. if branch.len > 1:
  586. addFact(tracked.guards, branch.sons[0])
  587. setLen(tracked.init, oldState)
  588. for i in 0 ..< branch.len:
  589. track(tracked, branch.sons[i])
  590. if not breaksBlock(branch.lastSon): inc toCover
  591. for i in oldState..<tracked.init.len:
  592. addToIntersection(inter, tracked.init[i])
  593. setLen(tracked.init, oldState)
  594. if lastSon(n).len == 1:
  595. for id, count in items(inter):
  596. if count >= toCover: tracked.init.add id
  597. # else we can't merge as it is not exhaustive
  598. setLen(tracked.guards.s, oldFacts)
  599. proc trackBlock(tracked: PEffects, n: PNode) =
  600. if n.kind in {nkStmtList, nkStmtListExpr}:
  601. var oldState = -1
  602. for i in 0..<n.len:
  603. if hasSubnodeWith(n.sons[i], nkBreakStmt):
  604. # block:
  605. # x = def
  606. # if ...: ... break # some nested break
  607. # y = def
  608. # --> 'y' not defined after block!
  609. if oldState < 0: oldState = tracked.init.len
  610. track(tracked, n.sons[i])
  611. if oldState > 0: setLen(tracked.init, oldState)
  612. else:
  613. track(tracked, n)
  614. proc isTrue*(n: PNode): bool =
  615. n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
  616. n.kind == nkIntLit and n.intVal != 0
  617. proc paramType(op: PType, i: int): PType =
  618. if op != nil and i < op.len: result = op.sons[i]
  619. proc cstringCheck(tracked: PEffects; n: PNode) =
  620. if n.sons[0].typ.kind == tyCString and (let a = skipConv(n[1]);
  621. a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}):
  622. message(tracked.config, n.info, warnUnsafeCode, renderTree(n))
  623. proc track(tracked: PEffects, n: PNode) =
  624. case n.kind
  625. of nkSym:
  626. useVar(tracked, n)
  627. of nkRaiseStmt:
  628. n.sons[0].info = n.info
  629. #throws(tracked.exc, n.sons[0])
  630. addEffect(tracked, n.sons[0], useLineInfo=false)
  631. for i in 0 ..< safeLen(n):
  632. track(tracked, n.sons[i])
  633. of nkCallKinds:
  634. if getConstExpr(tracked.owner_module, n, tracked.graph) != nil:
  635. return
  636. # p's effects are ours too:
  637. var a = n.sons[0]
  638. let op = a.typ
  639. if a.kind == nkCast and a[1].typ.kind == tyProc:
  640. a = a[1]
  641. # XXX: in rare situations, templates and macros will reach here after
  642. # calling getAst(templateOrMacro()). Currently, templates and macros
  643. # are indistinguishable from normal procs (both have tyProc type) and
  644. # we can detect them only by checking for attached nkEffectList.
  645. if op != nil and op.kind == tyProc and op.n.sons[0].kind == nkEffectList:
  646. if a.kind == nkSym:
  647. if a.sym == tracked.owner: tracked.isRecursive = true
  648. # even for recursive calls we need to check the lock levels (!):
  649. mergeLockLevels(tracked, n, a.sym.getLockLevel)
  650. if sfSideEffect in a.sym.flags: markSideEffect(tracked, a)
  651. else:
  652. mergeLockLevels(tracked, n, op.lockLevel)
  653. var effectList = op.n.sons[0]
  654. if a.kind == nkSym and a.sym.kind == skMethod:
  655. propagateEffects(tracked, n, a.sym)
  656. elif isNoEffectList(effectList):
  657. if isForwardedProc(a):
  658. propagateEffects(tracked, n, a.sym)
  659. elif isIndirectCall(a, tracked.owner):
  660. assumeTheWorst(tracked, n, op)
  661. else:
  662. mergeEffects(tracked, effectList.sons[exceptionEffects], n)
  663. mergeTags(tracked, effectList.sons[tagEffects], n)
  664. if notGcSafe(op) and not importedFromC(a):
  665. # and it's not a recursive call:
  666. if not (a.kind == nkSym and a.sym == tracked.owner):
  667. if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
  668. markGcUnsafe(tracked, a)
  669. if tfNoSideEffect notin op.flags and not importedFromC(a):
  670. # and it's not a recursive call:
  671. if not (a.kind == nkSym and a.sym == tracked.owner):
  672. markSideEffect(tracked, a)
  673. if a.kind != nkSym or a.sym.magic != mNBindSym:
  674. for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
  675. if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
  676. # may not look like an assignment, but it is:
  677. let arg = n.sons[1]
  678. initVarViaNew(tracked, arg)
  679. if arg.typ.len != 0 and {tfNeedsInit} * arg.typ.lastSon.flags != {}:
  680. if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
  681. n[2].intVal == 0:
  682. # var s: seq[notnil]; newSeq(s, 0) is a special case!
  683. discard
  684. else:
  685. message(tracked.config, arg.info, warnProveInit, $arg)
  686. for i in 0 ..< safeLen(n):
  687. track(tracked, n.sons[i])
  688. of nkDotExpr:
  689. guardDotAccess(tracked, n)
  690. for i in 0 ..< len(n): track(tracked, n.sons[i])
  691. of nkCheckedFieldExpr:
  692. track(tracked, n.sons[0])
  693. if warnProveField in tracked.config.notes:
  694. checkFieldAccess(tracked.guards, n, tracked.config)
  695. of nkTryStmt: trackTryStmt(tracked, n)
  696. of nkPragma: trackPragmaStmt(tracked, n)
  697. of nkAsgn, nkFastAsgn:
  698. track(tracked, n.sons[1])
  699. initVar(tracked, n.sons[0], volatileCheck=true)
  700. invalidateFacts(tracked.guards, n.sons[0])
  701. track(tracked, n.sons[0])
  702. addAsgnFact(tracked.guards, n.sons[0], n.sons[1])
  703. notNilCheck(tracked, n.sons[1], n.sons[0].typ)
  704. when false: cstringCheck(tracked, n)
  705. of nkVarSection, nkLetSection:
  706. for child in n:
  707. let last = lastSon(child)
  708. if last.kind != nkEmpty: track(tracked, last)
  709. if child.kind == nkIdentDefs and last.kind != nkEmpty:
  710. for i in 0 .. child.len-3:
  711. initVar(tracked, child.sons[i], volatileCheck=false)
  712. addAsgnFact(tracked.guards, child.sons[i], last)
  713. notNilCheck(tracked, last, child.sons[i].typ)
  714. elif child.kind == nkVarTuple and last.kind != nkEmpty:
  715. for i in 0 .. child.len-2:
  716. if child[i].kind == nkEmpty or
  717. child[i].kind == nkSym and child[i].sym.name.s == "_":
  718. continue
  719. initVar(tracked, child[i], volatileCheck=false)
  720. if last.kind in {nkPar, nkTupleConstr}:
  721. addAsgnFact(tracked.guards, child[i], last[i])
  722. notNilCheck(tracked, last[i], child[i].typ)
  723. # since 'var (a, b): T = ()' is not even allowed, there is always type
  724. # inference for (a, b) and thus no nil checking is necessary.
  725. of nkConstSection:
  726. for child in n:
  727. let last = lastSon(child)
  728. track(tracked, last)
  729. of nkCaseStmt: trackCase(tracked, n)
  730. of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
  731. of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n.sons[1])
  732. of nkWhileStmt:
  733. track(tracked, n.sons[0])
  734. # 'while true' loop?
  735. if isTrue(n.sons[0]):
  736. trackBlock(tracked, n.sons[1])
  737. else:
  738. # loop may never execute:
  739. let oldState = tracked.init.len
  740. let oldFacts = tracked.guards.s.len
  741. addFact(tracked.guards, n.sons[0])
  742. track(tracked, n.sons[1])
  743. setLen(tracked.init, oldState)
  744. setLen(tracked.guards.s, oldFacts)
  745. of nkForStmt, nkParForStmt:
  746. # we are very conservative here and assume the loop is never executed:
  747. let oldState = tracked.init.len
  748. for i in 0 ..< len(n):
  749. track(tracked, n.sons[i])
  750. setLen(tracked.init, oldState)
  751. of nkObjConstr:
  752. when false: track(tracked, n.sons[0])
  753. let oldFacts = tracked.guards.s.len
  754. for i in 1 ..< len(n):
  755. let x = n.sons[i]
  756. track(tracked, x)
  757. if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
  758. addDiscriminantFact(tracked.guards, x)
  759. setLen(tracked.guards.s, oldFacts)
  760. of nkPragmaBlock:
  761. let pragmaList = n.sons[0]
  762. let oldLocked = tracked.locked.len
  763. let oldLockLevel = tracked.currLockLevel
  764. var enforcedGcSafety = false
  765. var enforceNoSideEffects = false
  766. for i in 0 ..< pragmaList.len:
  767. let pragma = whichPragma(pragmaList.sons[i])
  768. if pragma == wLocks:
  769. lockLocations(tracked, pragmaList.sons[i])
  770. elif pragma == wGcSafe:
  771. enforcedGcSafety = true
  772. elif pragma == wNosideeffect:
  773. enforceNoSideEffects = true
  774. if enforcedGcSafety: tracked.inEnforcedGcSafe = true
  775. if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
  776. track(tracked, n.lastSon)
  777. if enforcedGcSafety: tracked.inEnforcedGcSafe = false
  778. if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false
  779. setLen(tracked.locked, oldLocked)
  780. tracked.currLockLevel = oldLockLevel
  781. of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
  782. nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
  783. discard
  784. of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv:
  785. if n.len == 2: track(tracked, n.sons[1])
  786. of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
  787. if n.len == 1: track(tracked, n.sons[0])
  788. else:
  789. for i in 0 ..< safeLen(n): track(tracked, n.sons[i])
  790. proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
  791. result = safeInheritanceDiff(g.excType(real), spec.typ) <= 0
  792. proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool;
  793. effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.}) =
  794. # check that any real exception is listed in 'spec'; mark those as used;
  795. # report any unused exception
  796. var used = initIntSet()
  797. for r in items(real):
  798. block search:
  799. for s in 0 ..< spec.len:
  800. if effectPredicate(g, spec[s], r):
  801. used.incl(s)
  802. break search
  803. # XXX call graph analysis would be nice here!
  804. pushInfoContext(g.config, spec.info)
  805. localError(g.config, r.info, errGenerated, msg & typeToString(r.typ))
  806. popInfoContext(g.config)
  807. # hint about unnecessarily listed exception types:
  808. if hints:
  809. for s in 0 ..< spec.len:
  810. if not used.contains(s):
  811. message(g.config, spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s]))
  812. proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) =
  813. ## checks for consistent effects for multi methods.
  814. let actual = branch.typ.n.sons[0]
  815. if actual.len != effectListLen: return
  816. let p = disp.ast.sons[pragmasPos]
  817. let raisesSpec = effectSpec(p, wRaises)
  818. if not isNil(raisesSpec):
  819. checkRaisesSpec(g, raisesSpec, actual.sons[exceptionEffects],
  820. "can raise an unlisted exception: ", hints=off, subtypeRelation)
  821. let tagsSpec = effectSpec(p, wTags)
  822. if not isNil(tagsSpec):
  823. checkRaisesSpec(g, tagsSpec, actual.sons[tagEffects],
  824. "can have an unlisted effect: ", hints=off, subtypeRelation)
  825. if sfThread in disp.flags and notGcSafe(branch.typ):
  826. localError(g.config, branch.info, "base method is GC-safe, but '$1' is not" %
  827. branch.name.s)
  828. if branch.typ.lockLevel > disp.typ.lockLevel:
  829. when true:
  830. message(g.config, branch.info, warnLockLevel,
  831. "base method has lock level $1, but dispatcher has $2" %
  832. [$branch.typ.lockLevel, $disp.typ.lockLevel])
  833. else:
  834. # XXX make this an error after bigbreak has been released:
  835. localError(g.config, branch.info,
  836. "base method has lock level $1, but dispatcher has $2" %
  837. [$branch.typ.lockLevel, $disp.typ.lockLevel])
  838. proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) =
  839. var effects = t.n[0]
  840. if t.kind != tyProc or effects.kind != nkEffectList: return
  841. if n.kind != nkEmpty:
  842. internalAssert g.config, effects.len == 0
  843. newSeq(effects.sons, effectListLen)
  844. let raisesSpec = effectSpec(n, wRaises)
  845. if not isNil(raisesSpec):
  846. effects[exceptionEffects] = raisesSpec
  847. let tagsSpec = effectSpec(n, wTags)
  848. if not isNil(tagsSpec):
  849. effects[tagEffects] = tagsSpec
  850. effects[pragmasEffects] = n
  851. proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
  852. newSeq(effects.sons, effectListLen)
  853. effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
  854. effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
  855. effects.sons[usesEffects] = g.emptyNode
  856. effects.sons[writeEffects] = g.emptyNode
  857. effects.sons[pragmasEffects] = g.emptyNode
  858. t.exc = effects.sons[exceptionEffects]
  859. t.tags = effects.sons[tagEffects]
  860. t.owner = s
  861. t.owner_module = s.getModule
  862. t.init = @[]
  863. t.guards.s = @[]
  864. t.guards.o = initOperators(g)
  865. t.locked = @[]
  866. t.graph = g
  867. t.config = g.config
  868. proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
  869. var effects = s.typ.n.sons[0]
  870. if effects.kind != nkEffectList: return
  871. # effects already computed?
  872. if sfForward in s.flags: return
  873. if effects.len == effectListLen: return
  874. var t: TEffects
  875. initEffects(g, effects, s, t)
  876. track(t, body)
  877. if not isEmptyType(s.typ.sons[0]) and
  878. {tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
  879. s.kind in {skProc, skFunc, skConverter, skMethod}:
  880. var res = s.ast.sons[resultPos].sym # get result symbol
  881. if res.id notin t.init:
  882. message(g.config, body.info, warnProveInit, "result")
  883. let p = s.ast.sons[pragmasPos]
  884. let raisesSpec = effectSpec(p, wRaises)
  885. if not isNil(raisesSpec):
  886. checkRaisesSpec(g, raisesSpec, t.exc, "can raise an unlisted exception: ",
  887. hints=on, subtypeRelation)
  888. # after the check, use the formal spec:
  889. effects.sons[exceptionEffects] = raisesSpec
  890. let tagsSpec = effectSpec(p, wTags)
  891. if not isNil(tagsSpec):
  892. checkRaisesSpec(g, tagsSpec, t.tags, "can have an unlisted effect: ",
  893. hints=off, subtypeRelation)
  894. # after the check, use the formal spec:
  895. effects.sons[tagEffects] = tagsSpec
  896. if sfThread in s.flags and t.gcUnsafe:
  897. if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions:
  898. #localError(s.info, "'$1' is not GC-safe" % s.name.s)
  899. listGcUnsafety(s, onlyWarning=false, g.config)
  900. else:
  901. listGcUnsafety(s, onlyWarning=true, g.config)
  902. #localError(s.info, warnGcUnsafe2, s.name.s)
  903. if sfNoSideEffect in s.flags and t.hasSideEffect:
  904. when false:
  905. listGcUnsafety(s, onlyWarning=false, g.config)
  906. else:
  907. localError(g.config, s.info, "'$1' can have side effects" % s.name.s)
  908. if not t.gcUnsafe:
  909. s.typ.flags.incl tfGcSafe
  910. if not t.hasSideEffect and sfSideEffect notin s.flags:
  911. s.typ.flags.incl tfNoSideEffect
  912. if s.typ.lockLevel == UnspecifiedLockLevel:
  913. s.typ.lockLevel = t.maxLockLevel
  914. elif t.maxLockLevel > s.typ.lockLevel:
  915. #localError(s.info,
  916. message(g.config, s.info, warnLockLevel,
  917. "declared lock level is $1, but real lock level is $2" %
  918. [$s.typ.lockLevel, $t.maxLockLevel])
  919. when defined(useDfa):
  920. if s.kind == skFunc:
  921. dataflowAnalysis(s, body)
  922. when false: trackWrites(s, body)
  923. proc trackTopLevelStmt*(g: ModuleGraph; module: PSym; n: PNode) =
  924. if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
  925. nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
  926. return
  927. var effects = newNode(nkEffectList, n.info)
  928. var t: TEffects
  929. initEffects(g, effects, module, t)
  930. t.isToplevel = true
  931. track(t, n)