sempass2.nim 35 KB

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