sempass2.nim 35 KB

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