sempass2.nim 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. import
  10. intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
  11. wordrecg, strutils, options, guards, lineinfos, semfold, semdata,
  12. modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling, tables,
  13. semstrictfuncs
  14. when defined(nimPreviewSlimSystem):
  15. import std/assertions
  16. when defined(useDfa):
  17. import dfa
  18. import liftdestructors
  19. include sinkparameter_inference
  20. #[ Second semantic checking pass over the AST. Necessary because the old
  21. way had some inherent problems. Performs:
  22. * effect+exception tracking
  23. * "usage before definition" checking
  24. * also now calls the "lift destructor logic" at strategic positions, this
  25. is about to be put into the spec:
  26. We treat assignment and sinks and destruction as identical.
  27. In the construct let/var x = expr() x's type is marked.
  28. In x = y the type of x is marked.
  29. For every sink parameter of type T T is marked.
  30. For every call f() the return type of f() is marked.
  31. ]#
  32. # ------------------------ exception and tag tracking -------------------------
  33. discard """
  34. exception tracking:
  35. a() # raises 'x', 'e'
  36. try:
  37. b() # raises 'e'
  38. except e:
  39. # must not undo 'e' here; hrm
  40. c()
  41. --> we need a stack of scopes for this analysis
  42. # XXX enhance the algorithm to care about 'dirty' expressions:
  43. lock a[i].L:
  44. inc i # mark 'i' dirty
  45. lock a[j].L:
  46. access a[i], a[j] # --> reject a[i]
  47. """
  48. type
  49. TEffects = object
  50. exc: PNode # stack of exceptions
  51. tags: PNode # list of tags
  52. forbids: PNode # list of tags
  53. bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn, inIfStmt, currentBlock: int
  54. owner: PSym
  55. ownerModule: PSym
  56. init: seq[int] # list of initialized variables
  57. scopes: Table[int, int] # maps var-id to its scope (see also `currentBlock`).
  58. guards: TModel # nested guards
  59. locked: seq[PNode] # locked locations
  60. gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
  61. isInnerProc: bool
  62. inEnforcedNoSideEffects: bool
  63. currOptions: TOptions
  64. config: ConfigRef
  65. graph: ModuleGraph
  66. c: PContext
  67. escapingParams: IntSet
  68. PEffects = var TEffects
  69. const
  70. errXCannotBeAssignedTo = "'$1' cannot be assigned to"
  71. errLetNeedsInit = "'let' symbol requires an initialization"
  72. proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
  73. if typ == nil or sfGeneratedOp in tracked.owner.flags:
  74. # don't create type bound ops for anything in a function with a `nodestroy` pragma
  75. # bug #21987
  76. return
  77. when false:
  78. let realType = typ.skipTypes(abstractInst)
  79. if realType.kind == tyRef and
  80. optSeqDestructors in tracked.config.globalOptions:
  81. createTypeBoundOps(tracked.graph, tracked.c, realType.lastSon, info)
  82. createTypeBoundOps(tracked.graph, tracked.c, typ, info, tracked.c.idgen)
  83. if (tfHasAsgn in typ.flags) or
  84. optSeqDestructors in tracked.config.globalOptions:
  85. tracked.owner.flags.incl sfInjectDestructors
  86. proc isLocalSym(a: PEffects, s: PSym): bool =
  87. s.typ != nil and (s.kind in {skLet, skVar, skResult} or (s.kind == skParam and isOutParam(s.typ))) and
  88. sfGlobal notin s.flags and s.owner == a.owner
  89. proc lockLocations(a: PEffects; pragma: PNode) =
  90. if pragma.kind != nkExprColonExpr:
  91. localError(a.config, pragma.info, "locks pragma without argument")
  92. return
  93. for x in pragma[1]:
  94. a.locked.add x
  95. proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
  96. # check whether the corresponding lock is held:
  97. for L in a.locked:
  98. if L.kind == nkSym and L.sym == guard: return
  99. # we allow accesses nevertheless in top level statements for
  100. # easier initialization:
  101. #if a.isTopLevel:
  102. # message(a.config, n.info, warnUnguardedAccess, renderTree(n))
  103. #else:
  104. if not a.isTopLevel:
  105. localError(a.config, n.info, "unguarded access: " & renderTree(n))
  106. # 'guard*' are checks which are concerned with 'guard' annotations
  107. # (var x{.guard: y.}: int)
  108. proc guardDotAccess(a: PEffects; n: PNode) =
  109. let ri = n[1]
  110. if ri.kind != nkSym or ri.sym.kind != skField: return
  111. var g = ri.sym.guard
  112. if g.isNil or a.isTopLevel: return
  113. # fixup guard:
  114. if g.kind == skUnknown:
  115. var field: PSym = nil
  116. var ty = n[0].typ.skipTypes(abstractPtrs)
  117. if ty.kind == tyTuple and not ty.n.isNil:
  118. field = lookupInRecord(ty.n, g.name)
  119. else:
  120. while ty != nil and ty.kind == tyObject:
  121. field = lookupInRecord(ty.n, g.name)
  122. if field != nil: break
  123. ty = ty[0]
  124. if ty == nil: break
  125. ty = ty.skipTypes(skipPtrs)
  126. if field == nil:
  127. localError(a.config, n.info, "invalid guard field: " & g.name.s)
  128. return
  129. g = field
  130. #ri.sym.guard = field
  131. # XXX unfortunately this is not correct for generic instantiations!
  132. if g.kind == skField:
  133. let dot = newNodeI(nkDotExpr, n.info, 2)
  134. dot[0] = n[0]
  135. dot[1] = newSymNode(g)
  136. dot.typ = g.typ
  137. for L in a.locked:
  138. #if a.guards.sameSubexprs(dot, L): return
  139. if guards.sameTree(dot, L): return
  140. localError(a.config, n.info, "unguarded access: " & renderTree(n))
  141. else:
  142. guardGlobal(a, n, g)
  143. proc makeVolatile(a: PEffects; s: PSym) {.inline.} =
  144. if a.inTryStmt > 0 and a.config.exc == excSetjmp:
  145. incl(s.flags, sfVolatile)
  146. proc varDecl(a: PEffects; n: PNode) {.inline.} =
  147. if n.kind == nkSym:
  148. a.scopes[n.sym.id] = a.currentBlock
  149. proc skipHiddenDeref(n: PNode): PNode {.inline.} =
  150. result = if n.kind == nkHiddenDeref: n[0] else: n
  151. proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
  152. let n = skipHiddenDeref(n)
  153. if n.kind != nkSym: return
  154. let s = n.sym
  155. if isLocalSym(a, s):
  156. if volatileCheck: makeVolatile(a, s)
  157. for x in a.init:
  158. if x == s.id:
  159. if strictDefs in a.c.features and s.kind == skLet:
  160. localError(a.config, n.info, errXCannotBeAssignedTo %
  161. renderTree(n, {renderNoComments}
  162. ))
  163. return
  164. a.init.add s.id
  165. if a.scopes.getOrDefault(s.id) == a.currentBlock:
  166. #[ Consider this case:
  167. var x: T
  168. while true:
  169. if cond:
  170. x = T() #1
  171. else:
  172. x = T() #2
  173. use x
  174. Even though both #1 and #2 are first writes we must use the `=copy`
  175. here so that the old value is destroyed because `x`'s destructor is
  176. run outside of the while loop. This is why we need the check here that
  177. the assignment is done in the same logical block as `x` was declared in.
  178. ]#
  179. n.flags.incl nfFirstWrite
  180. proc initVarViaNew(a: PEffects, n: PNode) =
  181. let n = skipHiddenDeref(n)
  182. if n.kind != nkSym: return
  183. let s = n.sym
  184. if {tfRequiresInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
  185. # 'x' is not nil, but that doesn't mean its "not nil" children
  186. # are initialized:
  187. initVar(a, n, volatileCheck=true)
  188. elif isLocalSym(a, s):
  189. makeVolatile(a, s)
  190. proc warnAboutGcUnsafe(n: PNode; conf: ConfigRef) =
  191. #assert false
  192. message(conf, n.info, warnGcUnsafe, renderTree(n))
  193. proc markGcUnsafe(a: PEffects; reason: PSym) =
  194. if not a.inEnforcedGcSafe:
  195. a.gcUnsafe = true
  196. if a.owner.kind in routineKinds: a.owner.gcUnsafetyReason = reason
  197. proc markGcUnsafe(a: PEffects; reason: PNode) =
  198. if not a.inEnforcedGcSafe:
  199. a.gcUnsafe = true
  200. if a.owner.kind in routineKinds:
  201. if reason.kind == nkSym:
  202. a.owner.gcUnsafetyReason = reason.sym
  203. else:
  204. a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, a.c.idgen,
  205. a.owner, reason.info, {})
  206. proc markSideEffect(a: PEffects; reason: PNode | PSym; useLoc: TLineInfo) =
  207. if not a.inEnforcedNoSideEffects:
  208. a.hasSideEffect = true
  209. if a.owner.kind in routineKinds:
  210. var sym: PSym
  211. when reason is PNode:
  212. if reason.kind == nkSym:
  213. sym = reason.sym
  214. else:
  215. let kind = if reason.kind == nkHiddenDeref: skParam else: skUnknown
  216. sym = newSym(kind, a.owner.name, a.c.idgen, a.owner, reason.info, {})
  217. else:
  218. sym = reason
  219. a.c.sideEffects.mgetOrPut(a.owner.id, @[]).add (useLoc, sym)
  220. when false: markGcUnsafe(a, reason)
  221. proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) =
  222. let u = s.gcUnsafetyReason
  223. if u != nil and not cycleCheck.containsOrIncl(u.id):
  224. let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
  225. case u.kind
  226. of skLet, skVar:
  227. if u.typ.skipTypes(abstractInst).kind == tyProc:
  228. message(conf, s.info, msgKind,
  229. "'$#' is not GC-safe as it calls '$#'" %
  230. [s.name.s, u.name.s])
  231. else:
  232. message(conf, s.info, msgKind,
  233. ("'$#' is not GC-safe as it accesses '$#'" &
  234. " which is a global using GC'ed memory") % [s.name.s, u.name.s])
  235. of routineKinds:
  236. # recursive call *always* produces only a warning so the full error
  237. # message is printed:
  238. if u.kind == skMethod and {sfBase, sfThread} * u.flags == {sfBase}:
  239. message(conf, u.info, msgKind,
  240. "Base method '$#' requires explicit '{.gcsafe.}' to be GC-safe" %
  241. [u.name.s])
  242. else:
  243. listGcUnsafety(u, true, cycleCheck, conf)
  244. message(conf, s.info, msgKind,
  245. "'$#' is not GC-safe as it calls '$#'" %
  246. [s.name.s, u.name.s])
  247. of skParam, skForVar:
  248. message(conf, s.info, msgKind,
  249. "'$#' is not GC-safe as it performs an indirect call via '$#'" %
  250. [s.name.s, u.name.s])
  251. else:
  252. message(conf, u.info, msgKind,
  253. "'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
  254. proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) =
  255. var cycleCheck = initIntSet()
  256. listGcUnsafety(s, onlyWarning, cycleCheck, conf)
  257. proc listSideEffects(result: var string; s: PSym; cycleCheck: var IntSet;
  258. conf: ConfigRef; context: PContext; indentLevel: int) =
  259. template addHint(msg; lineInfo; sym; level = indentLevel) =
  260. result.addf("$# $# Hint: '$#' $#\n", repeat(">", level), conf $ lineInfo, sym, msg)
  261. if context.sideEffects.hasKey(s.id):
  262. for (useLineInfo, u) in context.sideEffects[s.id]:
  263. if u != nil and not cycleCheck.containsOrIncl(u.id):
  264. case u.kind
  265. of skLet, skVar:
  266. addHint("accesses global state '$#'" % u.name.s, useLineInfo, s.name.s)
  267. addHint("accessed by '$#'" % s.name.s, u.info, u.name.s, indentLevel + 1)
  268. of routineKinds:
  269. addHint("calls `.sideEffect` '$#'" % u.name.s, useLineInfo, s.name.s)
  270. addHint("called by '$#'" % s.name.s, u.info, u.name.s, indentLevel + 1)
  271. listSideEffects(result, u, cycleCheck, conf, context, indentLevel + 2)
  272. of skParam, skForVar:
  273. addHint("calls routine via hidden pointer indirection", useLineInfo, s.name.s)
  274. else:
  275. addHint("calls routine via pointer indirection", useLineInfo, s.name.s)
  276. proc listSideEffects(result: var string; s: PSym; conf: ConfigRef; context: PContext) =
  277. var cycleCheck = initIntSet()
  278. result.addf("'$#' can have side effects\n", s.name.s)
  279. listSideEffects(result, s, cycleCheck, conf, context, 1)
  280. proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) =
  281. if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
  282. s.magic != mNimvm:
  283. if s.guard != nil: guardGlobal(a, n, s.guard)
  284. if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
  285. (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
  286. #if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n)
  287. markGcUnsafe(a, s)
  288. markSideEffect(a, s, n.info)
  289. if s.owner != a.owner and s.kind in {skVar, skLet, skForVar, skResult, skParam} and
  290. {sfGlobal, sfThread} * s.flags == {}:
  291. a.isInnerProc = true
  292. proc useVar(a: PEffects, n: PNode) =
  293. let s = n.sym
  294. if a.inExceptOrFinallyStmt > 0:
  295. incl s.flags, sfUsedInFinallyOrExcept
  296. if isLocalSym(a, s):
  297. if sfNoInit in s.flags:
  298. # If the variable is explicitly marked as .noinit. do not emit any error
  299. a.init.add s.id
  300. elif s.id notin a.init:
  301. if s.typ.requiresInit:
  302. message(a.config, n.info, warnProveInit, s.name.s)
  303. elif a.leftPartOfAsgn <= 0:
  304. if strictDefs in a.c.features:
  305. if s.kind == skLet:
  306. localError(a.config, n.info, errLetNeedsInit)
  307. else:
  308. message(a.config, n.info, warnUninit, s.name.s)
  309. # prevent superfluous warnings about the same variable:
  310. a.init.add s.id
  311. useVarNoInitCheck(a, n, s)
  312. type
  313. BreakState = enum
  314. bsNone
  315. bsBreakOrReturn
  316. bsNoReturn
  317. type
  318. TIntersection = seq[tuple[id, count: int]] # a simple count table
  319. proc addToIntersection(inter: var TIntersection, s: int, state: BreakState) =
  320. for j in 0..<inter.len:
  321. if s == inter[j].id:
  322. if state == bsNone:
  323. inc inter[j].count
  324. return
  325. if state == bsNone:
  326. inter.add((id: s, count: 1))
  327. else:
  328. inter.add((id: s, count: 0))
  329. proc throws(tracked, n, orig: PNode) =
  330. if n.typ == nil or n.typ.kind != tyError:
  331. if orig != nil:
  332. let x = copyTree(orig)
  333. x.typ = n.typ
  334. tracked.add x
  335. else:
  336. tracked.add n
  337. proc getEbase(g: ModuleGraph; info: TLineInfo): PType =
  338. result = g.sysTypeFromName(info, "Exception")
  339. proc excType(g: ModuleGraph; n: PNode): PType =
  340. # reraise is like raising E_Base:
  341. let t = if n.kind == nkEmpty or n.typ.isNil: getEbase(g, n.info) else: n.typ
  342. result = skipTypes(t, skipPtrs)
  343. proc createRaise(g: ModuleGraph; n: PNode): PNode =
  344. result = newNode(nkType)
  345. result.typ = getEbase(g, n.info)
  346. if not n.isNil: result.info = n.info
  347. proc createTag(g: ModuleGraph; n: PNode): PNode =
  348. result = newNode(nkType)
  349. result.typ = g.sysTypeFromName(n.info, "RootEffect")
  350. if not n.isNil: result.info = n.info
  351. proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) =
  352. #assert e.kind != nkRaiseStmt
  353. var aa = a.exc
  354. for i in a.bottom..<aa.len:
  355. # we only track the first node that can have the effect E in order
  356. # to safe space and time.
  357. if sameType(a.graph.excType(aa[i]), a.graph.excType(e)): return
  358. if e.typ != nil:
  359. if not isDefectException(e.typ):
  360. throws(a.exc, e, comesFrom)
  361. proc addTag(a: PEffects, e, comesFrom: PNode) =
  362. var aa = a.tags
  363. for i in 0..<aa.len:
  364. # we only track the first node that can have the effect E in order
  365. # to safe space and time.
  366. if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)): return
  367. throws(a.tags, e, comesFrom)
  368. proc addNotTag(a: PEffects, e, comesFrom: PNode) =
  369. var aa = a.forbids
  370. for i in 0..<aa.len:
  371. if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)): return
  372. throws(a.forbids, e, comesFrom)
  373. proc mergeRaises(a: PEffects, b, comesFrom: PNode) =
  374. if b.isNil:
  375. addRaiseEffect(a, createRaise(a.graph, comesFrom), comesFrom)
  376. else:
  377. for effect in items(b): addRaiseEffect(a, effect, comesFrom)
  378. proc mergeTags(a: PEffects, b, comesFrom: PNode) =
  379. if b.isNil:
  380. addTag(a, createTag(a.graph, comesFrom), comesFrom)
  381. else:
  382. for effect in items(b): addTag(a, effect, comesFrom)
  383. proc listEffects(a: PEffects) =
  384. for e in items(a.exc): message(a.config, e.info, hintUser, typeToString(e.typ))
  385. for e in items(a.tags): message(a.config, e.info, hintUser, typeToString(e.typ))
  386. for e in items(a.forbids): message(a.config, e.info, hintUser, typeToString(e.typ))
  387. proc catches(tracked: PEffects, e: PType) =
  388. let e = skipTypes(e, skipPtrs)
  389. var L = tracked.exc.len
  390. var i = tracked.bottom
  391. while i < L:
  392. # r supertype of e?
  393. if safeInheritanceDiff(tracked.graph.excType(tracked.exc[i]), e) <= 0:
  394. tracked.exc[i] = tracked.exc[L-1]
  395. dec L
  396. else:
  397. inc i
  398. if tracked.exc.len > 0:
  399. setLen(tracked.exc.sons, L)
  400. else:
  401. assert L == 0
  402. proc catchesAll(tracked: PEffects) =
  403. if tracked.exc.len > 0:
  404. setLen(tracked.exc.sons, tracked.bottom)
  405. proc track(tracked: PEffects, n: PNode)
  406. proc trackTryStmt(tracked: PEffects, n: PNode) =
  407. let oldBottom = tracked.bottom
  408. tracked.bottom = tracked.exc.len
  409. let oldState = tracked.init.len
  410. var inter: TIntersection = @[]
  411. inc tracked.inTryStmt
  412. track(tracked, n[0])
  413. dec tracked.inTryStmt
  414. for i in oldState..<tracked.init.len:
  415. addToIntersection(inter, tracked.init[i], bsNone)
  416. var branches = 1
  417. var hasFinally = false
  418. inc tracked.inExceptOrFinallyStmt
  419. # Collect the exceptions caught by the except branches
  420. for i in 1..<n.len:
  421. let b = n[i]
  422. if b.kind == nkExceptBranch:
  423. inc branches
  424. if b.len == 1:
  425. catchesAll(tracked)
  426. else:
  427. for j in 0..<b.len - 1:
  428. if b[j].isInfixAs():
  429. assert(b[j][1].kind == nkType)
  430. catches(tracked, b[j][1].typ)
  431. createTypeBoundOps(tracked, b[j][2].typ, b[j][2].info)
  432. else:
  433. assert(b[j].kind == nkType)
  434. catches(tracked, b[j].typ)
  435. else:
  436. assert b.kind == nkFinally
  437. # Add any other exception raised in the except bodies
  438. for i in 1..<n.len:
  439. let b = n[i]
  440. if b.kind == nkExceptBranch:
  441. setLen(tracked.init, oldState)
  442. for j in 0..<b.len - 1:
  443. if b[j].isInfixAs(): # skips initialization checks
  444. assert(b[j][2].kind == nkSym)
  445. tracked.init.add b[j][2].sym.id
  446. track(tracked, b[^1])
  447. for i in oldState..<tracked.init.len:
  448. addToIntersection(inter, tracked.init[i], bsNone)
  449. else:
  450. setLen(tracked.init, oldState)
  451. track(tracked, b[^1])
  452. hasFinally = true
  453. tracked.bottom = oldBottom
  454. dec tracked.inExceptOrFinallyStmt
  455. if not hasFinally:
  456. setLen(tracked.init, oldState)
  457. for id, count in items(inter):
  458. if count == branches: tracked.init.add id
  459. proc isIndirectCall(tracked: PEffects; n: PNode): bool =
  460. # we don't count f(...) as an indirect call if 'f' is an parameter.
  461. # Instead we track expressions of type tyProc too. See the manual for
  462. # details:
  463. if n.kind != nkSym:
  464. result = true
  465. elif n.sym.kind == skParam:
  466. if laxEffects notin tracked.c.config.legacyFeatures:
  467. if tracked.owner == n.sym.owner and sfEffectsDelayed in n.sym.flags:
  468. result = false # it is not a harmful call
  469. else:
  470. result = true
  471. else:
  472. result = tracked.owner != n.sym.owner or tracked.owner == nil
  473. elif n.sym.kind notin routineKinds:
  474. result = true
  475. else:
  476. result = false
  477. proc isForwardedProc(n: PNode): bool =
  478. result = n.kind == nkSym and sfForward in n.sym.flags
  479. proc trackPragmaStmt(tracked: PEffects, n: PNode) =
  480. for i in 0..<n.len:
  481. var it = n[i]
  482. let pragma = whichPragma(it)
  483. if pragma == wEffects:
  484. # list the computed effects up to here:
  485. listEffects(tracked)
  486. template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
  487. proc importedFromC(n: PNode): bool =
  488. # when imported from C, we assume GC-safety.
  489. result = n.kind == nkSym and sfImportc in n.sym.flags
  490. proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
  491. let pragma = s.ast[pragmasPos]
  492. let spec = effectSpec(pragma, wRaises)
  493. mergeRaises(tracked, spec, n)
  494. let tagSpec = effectSpec(pragma, wTags)
  495. mergeTags(tracked, tagSpec, n)
  496. if notGcSafe(s.typ) and sfImportc notin s.flags:
  497. if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
  498. markGcUnsafe(tracked, s)
  499. if tfNoSideEffect notin s.typ.flags:
  500. markSideEffect(tracked, s, n.info)
  501. proc procVarCheck(n: PNode; conf: ConfigRef) =
  502. if n.kind in nkSymChoices:
  503. for x in n: procVarCheck(x, conf)
  504. elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds:
  505. localError(conf, n.info, ("'$1' is a built-in and cannot be used as " &
  506. "a first-class procedure") % n.sym.name.s)
  507. proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
  508. let n = n.skipConv
  509. if paramType.isNil or paramType.kind != tyTypeDesc:
  510. procVarCheck skipConvCastAndClosure(n), tracked.config
  511. #elif n.kind in nkSymChoices:
  512. # echo "came here"
  513. let paramType = paramType.skipTypesOrNil(abstractInst)
  514. if paramType != nil and tfNotNil in paramType.flags and n.typ != nil:
  515. let ntyp = n.typ.skipTypesOrNil({tyVar, tyLent, tySink})
  516. if ntyp != nil and tfNotNil notin ntyp.flags:
  517. if isAddrNode(n):
  518. # addr(x[]) can't be proven, but addr(x) can:
  519. if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
  520. elif (n.kind == nkSym and n.sym.kind in routineKinds) or
  521. (n.kind in procDefs+{nkObjConstr, nkBracket, nkClosure, nkStrLit..nkTripleStrLit}) or
  522. (n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mArrToSeq) or
  523. n.typ.kind == tyTypeDesc:
  524. # 'p' is not nil obviously:
  525. return
  526. case impliesNotNil(tracked.guards, n)
  527. of impUnknown:
  528. message(tracked.config, n.info, errGenerated,
  529. "cannot prove '$1' is not nil" % n.renderTree)
  530. of impNo:
  531. message(tracked.config, n.info, errGenerated,
  532. "'$1' is provably nil" % n.renderTree)
  533. of impYes: discard
  534. proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
  535. addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
  536. addTag(tracked, createTag(tracked.graph, n), nil)
  537. proc isOwnedProcVar(tracked: PEffects; n: PNode): bool =
  538. # XXX prove the soundness of this effect system rule
  539. result = n.kind == nkSym and n.sym.kind == skParam and
  540. tracked.owner == n.sym.owner
  541. #if result and sfPolymorphic notin n.sym.flags:
  542. # echo tracked.config $ n.info, " different here!"
  543. if laxEffects notin tracked.c.config.legacyFeatures:
  544. result = result and sfEffectsDelayed in n.sym.flags
  545. proc isNoEffectList(n: PNode): bool {.inline.} =
  546. assert n.kind == nkEffectList
  547. n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil and n[forbiddenEffects] == nil)
  548. proc isTrival(caller: PNode): bool {.inline.} =
  549. result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved, mSwap}
  550. proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; argIndex: int; caller: PNode) =
  551. let a = skipConvCastAndClosure(n)
  552. let op = a.typ
  553. let param = if formals != nil and argIndex < formals.len and formals.n != nil: formals.n[argIndex].sym else: nil
  554. # assume indirect calls are taken here:
  555. if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and
  556. not isTrival(caller) and
  557. ((param != nil and sfEffectsDelayed in param.flags) or laxEffects in tracked.c.config.legacyFeatures):
  558. internalAssert tracked.config, op.n[0].kind == nkEffectList
  559. var effectList = op.n[0]
  560. var s = n.skipConv
  561. if s.kind == nkCast and s[1].typ.kind == tyProc:
  562. s = s[1]
  563. if s.kind == nkSym and s.sym.kind in routineKinds and isNoEffectList(effectList):
  564. propagateEffects(tracked, n, s.sym)
  565. elif isNoEffectList(effectList):
  566. if isForwardedProc(n):
  567. # we have no explicit effects but it's a forward declaration and so it's
  568. # stated there are no additional effects, so simply propagate them:
  569. propagateEffects(tracked, n, n.sym)
  570. elif not isOwnedProcVar(tracked, a):
  571. # we have no explicit effects so assume the worst:
  572. assumeTheWorst(tracked, n, op)
  573. # assume GcUnsafe unless in its type; 'forward' does not matter:
  574. if notGcSafe(op) and not isOwnedProcVar(tracked, a):
  575. if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
  576. markGcUnsafe(tracked, a)
  577. elif tfNoSideEffect notin op.flags and not isOwnedProcVar(tracked, a):
  578. markSideEffect(tracked, a, n.info)
  579. else:
  580. mergeRaises(tracked, effectList[exceptionEffects], n)
  581. mergeTags(tracked, effectList[tagEffects], n)
  582. if notGcSafe(op):
  583. if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
  584. markGcUnsafe(tracked, a)
  585. elif tfNoSideEffect notin op.flags:
  586. markSideEffect(tracked, a, n.info)
  587. let paramType = if formals != nil and argIndex < formals.len: formals[argIndex] else: nil
  588. if paramType != nil and paramType.kind in {tyVar}:
  589. invalidateFacts(tracked.guards, n)
  590. if n.kind == nkSym and isLocalSym(tracked, n.sym):
  591. makeVolatile(tracked, n.sym)
  592. if paramType != nil and paramType.kind == tyProc and tfGcSafe in paramType.flags:
  593. let argtype = skipTypes(a.typ, abstractInst)
  594. # XXX figure out why this can be a non tyProc here. See httpclient.nim for an
  595. # example that triggers it.
  596. if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
  597. localError(tracked.config, n.info, $n & " is not GC safe")
  598. notNilCheck(tracked, n, paramType)
  599. proc breaksBlock(n: PNode): BreakState =
  600. # semantic check doesn't allow statements after raise, break, return or
  601. # call to noreturn proc, so it is safe to check just the last statements
  602. var it = n
  603. while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
  604. it = it.lastSon
  605. case it.kind
  606. of nkBreakStmt, nkReturnStmt:
  607. result = bsBreakOrReturn
  608. of nkRaiseStmt:
  609. result = bsNoReturn
  610. of nkCallKinds:
  611. if it[0].kind == nkSym and sfNoReturn in it[0].sym.flags:
  612. result = bsNoReturn
  613. else:
  614. result = bsNone
  615. else:
  616. result = bsNone
  617. proc addIdToIntersection(tracked: PEffects, inter: var TIntersection, resCounter: var int,
  618. hasBreaksBlock: BreakState, oldState: int, resSym: PSym, hasResult: bool) =
  619. if hasResult:
  620. var alreadySatisfy = false
  621. if hasBreaksBlock == bsNoReturn:
  622. alreadySatisfy = true
  623. inc resCounter
  624. for i in oldState..<tracked.init.len:
  625. if tracked.init[i] == resSym.id:
  626. if not alreadySatisfy:
  627. inc resCounter
  628. alreadySatisfy = true
  629. else:
  630. addToIntersection(inter, tracked.init[i], hasBreaksBlock)
  631. else:
  632. for i in oldState..<tracked.init.len:
  633. addToIntersection(inter, tracked.init[i], hasBreaksBlock)
  634. template hasResultSym(s: PSym): bool =
  635. s != nil and s.kind in {skProc, skFunc, skConverter, skMethod} and
  636. not isEmptyType(s.typ[0])
  637. proc trackCase(tracked: PEffects, n: PNode) =
  638. track(tracked, n[0])
  639. inc tracked.inIfStmt
  640. let oldState = tracked.init.len
  641. let oldFacts = tracked.guards.s.len
  642. let stringCase = n[0].typ != nil and skipTypes(n[0].typ,
  643. abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString, tyCstring}
  644. let interesting = not stringCase and interestingCaseExpr(n[0]) and
  645. (tracked.config.hasWarn(warnProveField) or strictCaseObjects in tracked.c.features)
  646. var inter: TIntersection = @[]
  647. var toCover = 0
  648. let hasResult = hasResultSym(tracked.owner)
  649. let resSym = if hasResult: tracked.owner.ast[resultPos].sym else: nil
  650. var resCounter = 0
  651. for i in 1..<n.len:
  652. let branch = n[i]
  653. setLen(tracked.init, oldState)
  654. if interesting:
  655. setLen(tracked.guards.s, oldFacts)
  656. addCaseBranchFacts(tracked.guards, n, i)
  657. for i in 0..<branch.len:
  658. track(tracked, branch[i])
  659. let hasBreaksBlock = breaksBlock(branch.lastSon)
  660. if hasBreaksBlock == bsNone:
  661. inc toCover
  662. addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)
  663. setLen(tracked.init, oldState)
  664. if not stringCase or lastSon(n).kind == nkElse:
  665. if hasResult and resCounter == n.len-1:
  666. tracked.init.add resSym.id
  667. for id, count in items(inter):
  668. if count >= toCover: tracked.init.add id
  669. # else we can't merge
  670. setLen(tracked.guards.s, oldFacts)
  671. dec tracked.inIfStmt
  672. proc trackIf(tracked: PEffects, n: PNode) =
  673. track(tracked, n[0][0])
  674. inc tracked.inIfStmt
  675. let oldFacts = tracked.guards.s.len
  676. addFact(tracked.guards, n[0][0])
  677. let oldState = tracked.init.len
  678. let hasResult = hasResultSym(tracked.owner)
  679. let resSym = if hasResult: tracked.owner.ast[resultPos].sym else: nil
  680. var resCounter = 0
  681. var inter: TIntersection = @[]
  682. var toCover = 0
  683. track(tracked, n[0][1])
  684. let hasBreaksBlock = breaksBlock(n[0][1])
  685. if hasBreaksBlock == bsNone:
  686. inc toCover
  687. addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)
  688. for i in 1..<n.len:
  689. let branch = n[i]
  690. setLen(tracked.guards.s, oldFacts)
  691. for j in 0..i-1:
  692. addFactNeg(tracked.guards, n[j][0])
  693. if branch.len > 1:
  694. addFact(tracked.guards, branch[0])
  695. setLen(tracked.init, oldState)
  696. for i in 0..<branch.len:
  697. track(tracked, branch[i])
  698. let hasBreaksBlock = breaksBlock(branch.lastSon)
  699. if hasBreaksBlock == bsNone:
  700. inc toCover
  701. addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)
  702. setLen(tracked.init, oldState)
  703. if lastSon(n).len == 1:
  704. if hasResult and resCounter == n.len:
  705. tracked.init.add resSym.id
  706. for id, count in items(inter):
  707. if count >= toCover: tracked.init.add id
  708. # else we can't merge as it is not exhaustive
  709. setLen(tracked.guards.s, oldFacts)
  710. dec tracked.inIfStmt
  711. proc trackBlock(tracked: PEffects, n: PNode) =
  712. if n.kind in {nkStmtList, nkStmtListExpr}:
  713. var oldState = -1
  714. for i in 0..<n.len:
  715. if hasSubnodeWith(n[i], nkBreakStmt):
  716. # block:
  717. # x = def
  718. # if ...: ... break # some nested break
  719. # y = def
  720. # --> 'y' not defined after block!
  721. if oldState < 0: oldState = tracked.init.len
  722. track(tracked, n[i])
  723. if oldState > 0: setLen(tracked.init, oldState)
  724. else:
  725. track(tracked, n)
  726. proc cstringCheck(tracked: PEffects; n: PNode) =
  727. if n[0].typ.kind == tyCstring and (let a = skipConv(n[1]);
  728. a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}):
  729. message(tracked.config, n.info, warnUnsafeCode, renderTree(n))
  730. proc patchResult(c: PEffects; n: PNode) =
  731. if n.kind == nkSym and n.sym.kind == skResult:
  732. let fn = c.owner
  733. if fn != nil and fn.kind in routineKinds and fn.ast != nil and resultPos < fn.ast.len:
  734. n.sym = fn.ast[resultPos].sym
  735. else:
  736. localError(c.config, n.info, "routine has no return type, but .requires contains 'result'")
  737. else:
  738. for i in 0..<safeLen(n):
  739. patchResult(c, n[i])
  740. proc checkLe(c: PEffects; a, b: PNode) =
  741. case proveLe(c.guards, a, b)
  742. of impUnknown:
  743. #for g in c.guards.s:
  744. # if g != nil: echo "I Know ", g
  745. message(c.config, a.info, warnStaticIndexCheck,
  746. "cannot prove: " & $a & " <= " & $b)
  747. of impYes:
  748. discard
  749. of impNo:
  750. message(c.config, a.info, warnStaticIndexCheck,
  751. "can prove: " & $a & " > " & $b)
  752. proc checkBounds(c: PEffects; arr, idx: PNode) =
  753. checkLe(c, lowBound(c.config, arr), idx)
  754. checkLe(c, idx, highBound(c.config, arr, c.guards.g.operators))
  755. proc checkRange(c: PEffects; value: PNode; typ: PType) =
  756. let t = typ.skipTypes(abstractInst - {tyRange})
  757. if t.kind == tyRange:
  758. let lowBound = copyTree(t.n[0])
  759. lowBound.info = value.info
  760. let highBound = copyTree(t.n[1])
  761. highBound.info = value.info
  762. checkLe(c, lowBound, value)
  763. checkLe(c, value, highBound)
  764. #[
  765. proc passedToEffectsDelayedParam(tracked: PEffects; n: PNode) =
  766. let t = n.typ.skipTypes(abstractInst)
  767. if t.kind == tyProc:
  768. if n.kind == nkSym and tracked.owner == n.sym.owner and sfEffectsDelayed in n.sym.flags:
  769. discard "the arg is itself a delayed parameter, so do nothing"
  770. else:
  771. var effectList = t.n[0]
  772. if effectList.len == effectListLen:
  773. mergeRaises(tracked, effectList[exceptionEffects], n)
  774. mergeTags(tracked, effectList[tagEffects], n)
  775. if not importedFromC(n):
  776. if notGcSafe(t):
  777. if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
  778. markGcUnsafe(tracked, n)
  779. if tfNoSideEffect notin t.flags:
  780. markSideEffect(tracked, n, n.info)
  781. ]#
  782. proc checkForSink(tracked: PEffects; n: PNode) =
  783. if tracked.inIfStmt == 0 and optSinkInference in tracked.config.options:
  784. checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n)
  785. proc trackCall(tracked: PEffects; n: PNode) =
  786. template gcsafeAndSideeffectCheck() =
  787. if notGcSafe(op) and not importedFromC(a):
  788. # and it's not a recursive call:
  789. if not (a.kind == nkSym and a.sym == tracked.owner):
  790. if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
  791. markGcUnsafe(tracked, a)
  792. if tfNoSideEffect notin op.flags and not importedFromC(a):
  793. # and it's not a recursive call:
  794. if not (a.kind == nkSym and a.sym == tracked.owner):
  795. markSideEffect(tracked, a, n.info)
  796. # p's effects are ours too:
  797. var a = n[0]
  798. #if canRaise(a):
  799. # echo "this can raise ", tracked.config $ n.info
  800. let op = a.typ
  801. if n.typ != nil:
  802. if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
  803. createTypeBoundOps(tracked, n.typ, n.info)
  804. let notConstExpr = getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) == nil
  805. if notConstExpr:
  806. if a.kind == nkCast and a[1].typ.kind == tyProc:
  807. a = a[1]
  808. # XXX: in rare situations, templates and macros will reach here after
  809. # calling getAst(templateOrMacro()). Currently, templates and macros
  810. # are indistinguishable from normal procs (both have tyProc type) and
  811. # we can detect them only by checking for attached nkEffectList.
  812. if op != nil and op.kind == tyProc and op.n[0].kind == nkEffectList:
  813. if a.kind == nkSym:
  814. if a.sym == tracked.owner: tracked.isRecursive = true
  815. # even for recursive calls we need to check the lock levels (!):
  816. if sfSideEffect in a.sym.flags: markSideEffect(tracked, a, n.info)
  817. else:
  818. discard
  819. var effectList = op.n[0]
  820. if a.kind == nkSym and a.sym.kind == skMethod:
  821. if {sfBase, sfThread} * a.sym.flags == {sfBase}:
  822. if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
  823. markGcUnsafe(tracked, a)
  824. propagateEffects(tracked, n, a.sym)
  825. elif isNoEffectList(effectList):
  826. if isForwardedProc(a):
  827. propagateEffects(tracked, n, a.sym)
  828. elif isIndirectCall(tracked, a):
  829. assumeTheWorst(tracked, n, op)
  830. gcsafeAndSideeffectCheck()
  831. else:
  832. if laxEffects notin tracked.c.config.legacyFeatures and a.kind == nkSym and
  833. a.sym.kind in routineKinds:
  834. propagateEffects(tracked, n, a.sym)
  835. else:
  836. mergeRaises(tracked, effectList[exceptionEffects], n)
  837. mergeTags(tracked, effectList[tagEffects], n)
  838. gcsafeAndSideeffectCheck()
  839. if a.kind != nkSym or a.sym.magic notin {mNBindSym, mFinished, mExpandToAst, mQuoteAst}:
  840. for i in 1..<n.len:
  841. trackOperandForIndirectCall(tracked, n[i], op, i, a)
  842. if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
  843. # may not look like an assignment, but it is:
  844. let arg = n[1]
  845. initVarViaNew(tracked, arg)
  846. if arg.typ.len != 0 and {tfRequiresInit} * arg.typ.lastSon.flags != {}:
  847. if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
  848. n[2].intVal == 0:
  849. # var s: seq[notnil]; newSeq(s, 0) is a special case!
  850. discard
  851. else:
  852. message(tracked.config, arg.info, warnProveInit, $arg)
  853. # check required for 'nim check':
  854. if n[1].typ.len > 0:
  855. createTypeBoundOps(tracked, n[1].typ.lastSon, n.info)
  856. createTypeBoundOps(tracked, n[1].typ, n.info)
  857. # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'?
  858. elif a.kind == nkSym and a.sym.magic in {mArrGet, mArrPut} and
  859. optStaticBoundsCheck in tracked.currOptions:
  860. checkBounds(tracked, n[1], n[2])
  861. if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
  862. tracked.owner.kind != skMacro:
  863. var opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
  864. if a.sym.name.s == "=": opKind = attachedAsgn.int
  865. if opKind != -1:
  866. # rebind type bounds operations after createTypeBoundOps call
  867. let t = n[1].typ.skipTypes({tyAlias, tyVar})
  868. if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
  869. createTypeBoundOps(tracked, t, n.info)
  870. let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
  871. if op != nil:
  872. n[0].sym = op
  873. if op != nil and op.kind == tyProc:
  874. for i in 1..<min(n.safeLen, op.len):
  875. let paramType = op[i]
  876. case paramType.kind
  877. of tySink:
  878. createTypeBoundOps(tracked, paramType[0], n.info)
  879. checkForSink(tracked, n[i])
  880. of tyVar:
  881. if isOutParam(paramType):
  882. # consider this case: p(out x, x); we want to remark that 'x' is not
  883. # initialized until after the call. Since we do this after we analysed the
  884. # call, this is fine.
  885. initVar(tracked, n[i].skipAddr, false)
  886. if strictFuncs in tracked.c.features and not tracked.inEnforcedNoSideEffects and
  887. isDangerousLocation(n[i].skipAddr, tracked.owner):
  888. if sfNoSideEffect in tracked.owner.flags:
  889. localError(tracked.config, n[i].info,
  890. "cannot pass $1 to `var T` parameter within a strict func" % renderTree(n[i]))
  891. tracked.hasSideEffect = true
  892. else: discard
  893. if notConstExpr and (a.kind != nkSym or
  894. a.sym.magic notin {mRunnableExamples, mNBindSym, mExpandToAst, mQuoteAst}
  895. ):
  896. # tracked after out analysis
  897. for i in 0..<n.safeLen:
  898. track(tracked, n[i])
  899. type
  900. PragmaBlockContext = object
  901. oldLocked: int
  902. enforcedGcSafety, enforceNoSideEffects: bool
  903. oldExc, oldTags, oldForbids: int
  904. exc, tags, forbids: PNode
  905. proc createBlockContext(tracked: PEffects): PragmaBlockContext =
  906. var oldForbidsLen = 0
  907. if tracked.forbids != nil: oldForbidsLen = tracked.forbids.len
  908. result = PragmaBlockContext(oldLocked: tracked.locked.len,
  909. enforcedGcSafety: false, enforceNoSideEffects: false,
  910. oldExc: tracked.exc.len, oldTags: tracked.tags.len,
  911. oldForbids: oldForbidsLen)
  912. proc applyBlockContext(tracked: PEffects, bc: PragmaBlockContext) =
  913. if bc.enforcedGcSafety: tracked.inEnforcedGcSafe = true
  914. if bc.enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
  915. proc unapplyBlockContext(tracked: PEffects; bc: PragmaBlockContext) =
  916. if bc.enforcedGcSafety: tracked.inEnforcedGcSafe = false
  917. if bc.enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false
  918. setLen(tracked.locked, bc.oldLocked)
  919. if bc.exc != nil:
  920. # beware that 'raises: []' is very different from not saying
  921. # anything about 'raises' in the 'cast' at all. Same applies for 'tags'.
  922. setLen(tracked.exc.sons, bc.oldExc)
  923. for e in bc.exc:
  924. addRaiseEffect(tracked, e, e)
  925. if bc.tags != nil:
  926. setLen(tracked.tags.sons, bc.oldTags)
  927. for t in bc.tags:
  928. addTag(tracked, t, t)
  929. if bc.forbids != nil:
  930. setLen(tracked.forbids.sons, bc.oldForbids)
  931. for t in bc.forbids:
  932. addNotTag(tracked, t, t)
  933. proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
  934. case whichPragma(pragma)
  935. of wGcSafe:
  936. bc.enforcedGcSafety = true
  937. of wNoSideEffect:
  938. bc.enforceNoSideEffects = true
  939. of wTags:
  940. let n = pragma[1]
  941. if n.kind in {nkCurly, nkBracket}:
  942. bc.tags = n
  943. else:
  944. bc.tags = newNodeI(nkArgList, pragma.info)
  945. bc.tags.add n
  946. of wForbids:
  947. let n = pragma[1]
  948. if n.kind in {nkCurly, nkBracket}:
  949. bc.forbids = n
  950. else:
  951. bc.forbids = newNodeI(nkArgList, pragma.info)
  952. bc.forbids.add n
  953. of wRaises:
  954. let n = pragma[1]
  955. if n.kind in {nkCurly, nkBracket}:
  956. bc.exc = n
  957. else:
  958. bc.exc = newNodeI(nkArgList, pragma.info)
  959. bc.exc.add n
  960. of wUncheckedAssign:
  961. discard "handled in sempass1"
  962. else:
  963. localError(tracked.config, pragma.info,
  964. "invalid pragma block: " & $pragma)
  965. proc trackInnerProc(tracked: PEffects, n: PNode) =
  966. case n.kind
  967. of nkSym:
  968. let s = n.sym
  969. if s.kind == skParam and s.owner == tracked.owner:
  970. tracked.escapingParams.incl s.id
  971. of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
  972. discard
  973. of nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkLambda, nkFuncDef, nkDo:
  974. if n[0].kind == nkSym and n[0].sym.ast != nil:
  975. trackInnerProc(tracked, getBody(tracked.graph, n[0].sym))
  976. of nkTypeSection, nkMacroDef, nkTemplateDef, nkError,
  977. nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
  978. nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
  979. nkTypeOfExpr, nkMixinStmt, nkBindStmt:
  980. discard
  981. else:
  982. for ch in n: trackInnerProc(tracked, ch)
  983. proc allowCStringConv(n: PNode): bool =
  984. case n.kind
  985. of nkStrLit..nkTripleStrLit: result = true
  986. of nkSym: result = n.sym.kind in {skConst, skParam}
  987. of nkAddr: result = isCharArrayPtr(n.typ, true)
  988. of nkCallKinds:
  989. result = isCharArrayPtr(n.typ, n[0].kind == nkSym and n[0].sym.magic == mAddr)
  990. else: result = isCharArrayPtr(n.typ, false)
  991. proc track(tracked: PEffects, n: PNode) =
  992. case n.kind
  993. of nkSym:
  994. useVar(tracked, n)
  995. if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
  996. tracked.owner.flags.incl sfInjectDestructors
  997. # bug #15038: ensure consistency
  998. if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ
  999. of nkHiddenAddr, nkAddr:
  1000. if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym):
  1001. useVarNoInitCheck(tracked, n[0], n[0].sym)
  1002. else:
  1003. track(tracked, n[0])
  1004. of nkRaiseStmt:
  1005. if n[0].kind != nkEmpty:
  1006. n[0].info = n.info
  1007. #throws(tracked.exc, n[0])
  1008. addRaiseEffect(tracked, n[0], n)
  1009. for i in 0..<n.safeLen:
  1010. track(tracked, n[i])
  1011. createTypeBoundOps(tracked, n[0].typ, n.info)
  1012. else:
  1013. # A `raise` with no arguments means we're going to re-raise the exception
  1014. # being handled or, if outside of an `except` block, a `ReraiseDefect`.
  1015. # Here we add a `Exception` tag in order to cover both the cases.
  1016. addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
  1017. of nkCallKinds:
  1018. trackCall(tracked, n)
  1019. of nkDotExpr:
  1020. guardDotAccess(tracked, n)
  1021. for i in 0..<n.len: track(tracked, n[i])
  1022. of nkCheckedFieldExpr:
  1023. track(tracked, n[0])
  1024. if tracked.config.hasWarn(warnProveField) or strictCaseObjects in tracked.c.features:
  1025. checkFieldAccess(tracked.guards, n, tracked.config, strictCaseObjects in tracked.c.features)
  1026. of nkTryStmt: trackTryStmt(tracked, n)
  1027. of nkPragma: trackPragmaStmt(tracked, n)
  1028. of nkAsgn, nkFastAsgn, nkSinkAsgn:
  1029. track(tracked, n[1])
  1030. initVar(tracked, n[0], volatileCheck=true)
  1031. invalidateFacts(tracked.guards, n[0])
  1032. inc tracked.leftPartOfAsgn
  1033. track(tracked, n[0])
  1034. dec tracked.leftPartOfAsgn
  1035. addAsgnFact(tracked.guards, n[0], n[1])
  1036. notNilCheck(tracked, n[1], n[0].typ)
  1037. when false: cstringCheck(tracked, n)
  1038. if tracked.owner.kind != skMacro and n[0].typ.kind notin {tyOpenArray, tyVarargs}:
  1039. createTypeBoundOps(tracked, n[0].typ, n.info)
  1040. if n[0].kind != nkSym or not isLocalSym(tracked, n[0].sym):
  1041. checkForSink(tracked, n[1])
  1042. if strictFuncs in tracked.c.features and not tracked.inEnforcedNoSideEffects and
  1043. isDangerousLocation(n[0], tracked.owner):
  1044. tracked.hasSideEffect = true
  1045. if sfNoSideEffect in tracked.owner.flags:
  1046. localError(tracked.config, n[0].info,
  1047. "cannot mutate location $1 within a strict func" % renderTree(n[0]))
  1048. of nkVarSection, nkLetSection:
  1049. for child in n:
  1050. let last = lastSon(child)
  1051. if last.kind != nkEmpty: track(tracked, last)
  1052. if tracked.owner.kind != skMacro:
  1053. if child.kind == nkVarTuple:
  1054. createTypeBoundOps(tracked, child[^1].typ, child.info)
  1055. for i in 0..<child.len-2:
  1056. createTypeBoundOps(tracked, child[i].typ, child.info)
  1057. else:
  1058. createTypeBoundOps(tracked, skipPragmaExpr(child[0]).typ, child.info)
  1059. if child.kind == nkIdentDefs:
  1060. for i in 0..<child.len-2:
  1061. let a = skipPragmaExpr(child[i])
  1062. varDecl(tracked, a)
  1063. if last.kind != nkEmpty:
  1064. initVar(tracked, a, volatileCheck=false)
  1065. addAsgnFact(tracked.guards, a, last)
  1066. notNilCheck(tracked, last, a.typ)
  1067. elif child.kind == nkVarTuple:
  1068. for i in 0..<child.len-1:
  1069. if child[i].kind == nkEmpty or
  1070. child[i].kind == nkSym and child[i].sym.name.id == ord(wUnderscore):
  1071. continue
  1072. varDecl(tracked, child[i])
  1073. if last.kind != nkEmpty:
  1074. initVar(tracked, child[i], volatileCheck=false)
  1075. if last.kind in {nkPar, nkTupleConstr}:
  1076. addAsgnFact(tracked.guards, child[i], last[i])
  1077. notNilCheck(tracked, last[i], child[i].typ)
  1078. # since 'var (a, b): T = ()' is not even allowed, there is always type
  1079. # inference for (a, b) and thus no nil checking is necessary.
  1080. of nkConstSection:
  1081. for child in n:
  1082. let last = lastSon(child)
  1083. track(tracked, last)
  1084. of nkCaseStmt: trackCase(tracked, n)
  1085. of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
  1086. of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
  1087. of nkWhileStmt:
  1088. # 'while true' loop?
  1089. inc tracked.currentBlock
  1090. if isTrue(n[0]):
  1091. trackBlock(tracked, n[1])
  1092. else:
  1093. # loop may never execute:
  1094. let oldState = tracked.init.len
  1095. let oldFacts = tracked.guards.s.len
  1096. addFact(tracked.guards, n[0])
  1097. track(tracked, n[0])
  1098. track(tracked, n[1])
  1099. setLen(tracked.init, oldState)
  1100. setLen(tracked.guards.s, oldFacts)
  1101. dec tracked.currentBlock
  1102. of nkForStmt, nkParForStmt:
  1103. # we are very conservative here and assume the loop is never executed:
  1104. inc tracked.currentBlock
  1105. let oldState = tracked.init.len
  1106. let oldFacts = tracked.guards.s.len
  1107. let iterCall = n[n.len-2]
  1108. if optStaticBoundsCheck in tracked.currOptions and iterCall.kind in nkCallKinds:
  1109. let op = iterCall[0]
  1110. if op.kind == nkSym and fromSystem(op.sym):
  1111. let iterVar = n[0]
  1112. case op.sym.name.s
  1113. of "..", "countup", "countdown":
  1114. let lower = iterCall[1]
  1115. let upper = iterCall[2]
  1116. # for i in 0..n means 0 <= i and i <= n. Countdown is
  1117. # the same since only the iteration direction changes.
  1118. addFactLe(tracked.guards, lower, iterVar)
  1119. addFactLe(tracked.guards, iterVar, upper)
  1120. of "..<":
  1121. let lower = iterCall[1]
  1122. let upper = iterCall[2]
  1123. addFactLe(tracked.guards, lower, iterVar)
  1124. addFactLt(tracked.guards, iterVar, upper)
  1125. else: discard
  1126. for i in 0..<n.len-2:
  1127. let it = n[i]
  1128. track(tracked, it)
  1129. if tracked.owner.kind != skMacro:
  1130. if it.kind == nkVarTuple:
  1131. for x in it:
  1132. createTypeBoundOps(tracked, x.typ, x.info)
  1133. else:
  1134. createTypeBoundOps(tracked, it.typ, it.info)
  1135. let loopBody = n[^1]
  1136. if tracked.owner.kind != skMacro and iterCall.safeLen > 1:
  1137. # XXX this is a bit hacky:
  1138. if iterCall[1].typ != nil and iterCall[1].typ.skipTypes(abstractVar).kind notin {tyVarargs, tyOpenArray}:
  1139. createTypeBoundOps(tracked, iterCall[1].typ, iterCall[1].info)
  1140. track(tracked, iterCall)
  1141. track(tracked, loopBody)
  1142. setLen(tracked.init, oldState)
  1143. setLen(tracked.guards.s, oldFacts)
  1144. dec tracked.currentBlock
  1145. of nkObjConstr:
  1146. when false: track(tracked, n[0])
  1147. let oldFacts = tracked.guards.s.len
  1148. for i in 1..<n.len:
  1149. let x = n[i]
  1150. track(tracked, x)
  1151. if x[0].kind == nkSym and sfDiscriminant in x[0].sym.flags:
  1152. addDiscriminantFact(tracked.guards, x)
  1153. if tracked.owner.kind != skMacro:
  1154. createTypeBoundOps(tracked, x[1].typ, n.info)
  1155. if x.kind == nkExprColonExpr:
  1156. if x[0].kind == nkSym:
  1157. notNilCheck(tracked, x[1], x[0].sym.typ)
  1158. checkForSink(tracked, x[1])
  1159. else:
  1160. checkForSink(tracked, x)
  1161. setLen(tracked.guards.s, oldFacts)
  1162. if tracked.owner.kind != skMacro:
  1163. # XXX n.typ can be nil in runnableExamples, we need to do something about it.
  1164. if n.typ != nil and n.typ.skipTypes(abstractInst).kind == tyRef:
  1165. createTypeBoundOps(tracked, n.typ.lastSon, n.info)
  1166. createTypeBoundOps(tracked, n.typ, n.info)
  1167. of nkTupleConstr:
  1168. for i in 0..<n.len:
  1169. track(tracked, n[i])
  1170. notNilCheck(tracked, n[i].skipColon, n[i].typ)
  1171. if tracked.owner.kind != skMacro:
  1172. if n[i].kind == nkExprColonExpr:
  1173. createTypeBoundOps(tracked, n[i][0].typ, n.info)
  1174. else:
  1175. createTypeBoundOps(tracked, n[i].typ, n.info)
  1176. checkForSink(tracked, n[i])
  1177. of nkPragmaBlock:
  1178. let pragmaList = n[0]
  1179. var bc = createBlockContext(tracked)
  1180. for i in 0..<pragmaList.len:
  1181. let pragma = whichPragma(pragmaList[i])
  1182. case pragma
  1183. of wLocks:
  1184. lockLocations(tracked, pragmaList[i])
  1185. of wGcSafe:
  1186. bc.enforcedGcSafety = true
  1187. of wNoSideEffect:
  1188. bc.enforceNoSideEffects = true
  1189. of wCast:
  1190. castBlock(tracked, pragmaList[i][1], bc)
  1191. else:
  1192. discard
  1193. applyBlockContext(tracked, bc)
  1194. track(tracked, n.lastSon)
  1195. unapplyBlockContext(tracked, bc)
  1196. of nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkLambda, nkFuncDef, nkDo:
  1197. if n[0].kind == nkSym and n[0].sym.ast != nil:
  1198. trackInnerProc(tracked, getBody(tracked.graph, n[0].sym))
  1199. of nkTypeSection, nkMacroDef, nkTemplateDef:
  1200. discard
  1201. of nkCast:
  1202. if n.len == 2:
  1203. track(tracked, n[1])
  1204. if tracked.owner.kind != skMacro:
  1205. createTypeBoundOps(tracked, n.typ, n.info)
  1206. of nkHiddenStdConv, nkHiddenSubConv, nkConv:
  1207. if n.kind in {nkHiddenStdConv, nkHiddenSubConv} and
  1208. n.typ.skipTypes(abstractInst).kind == tyCstring and
  1209. not allowCStringConv(n[1]):
  1210. message(tracked.config, n.info, warnCstringConv,
  1211. "implicit conversion to 'cstring' from a non-const location: $1; this will become a compile time error in the future" %
  1212. $n[1])
  1213. if n.typ.skipTypes(abstractInst).kind == tyCstring and
  1214. isCharArrayPtr(n[1].typ, true):
  1215. message(tracked.config, n.info, warnPtrToCstringConv,
  1216. $n[1].typ)
  1217. let t = n.typ.skipTypes(abstractInst)
  1218. if t.kind == tyEnum:
  1219. if tfEnumHasHoles in t.flags:
  1220. message(tracked.config, n.info, warnHoleEnumConv, "conversion to enum with holes is unsafe: $1" % $n)
  1221. else:
  1222. message(tracked.config, n.info, warnAnyEnumConv, "enum conversion: $1" % $n)
  1223. if n.len == 2:
  1224. track(tracked, n[1])
  1225. if tracked.owner.kind != skMacro:
  1226. createTypeBoundOps(tracked, n.typ, n.info)
  1227. # This is a hacky solution in order to fix bug #13110. Hopefully
  1228. # a better solution will come up eventually.
  1229. if n[1].typ.kind != tyString:
  1230. createTypeBoundOps(tracked, n[1].typ, n[1].info)
  1231. if optStaticBoundsCheck in tracked.currOptions:
  1232. checkRange(tracked, n[1], n.typ)
  1233. of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
  1234. if n.len == 1:
  1235. track(tracked, n[0])
  1236. if tracked.owner.kind != skMacro:
  1237. createTypeBoundOps(tracked, n.typ, n.info)
  1238. createTypeBoundOps(tracked, n[0].typ, n[0].info)
  1239. if optStaticBoundsCheck in tracked.currOptions:
  1240. checkRange(tracked, n[0], n.typ)
  1241. of nkBracket:
  1242. for i in 0..<n.safeLen:
  1243. track(tracked, n[i])
  1244. checkForSink(tracked, n[i])
  1245. if tracked.owner.kind != skMacro:
  1246. createTypeBoundOps(tracked, n.typ, n.info)
  1247. of nkBracketExpr:
  1248. if optStaticBoundsCheck in tracked.currOptions and n.len == 2:
  1249. if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
  1250. checkBounds(tracked, n[0], n[1])
  1251. track(tracked, n[0])
  1252. dec tracked.leftPartOfAsgn
  1253. for i in 1 ..< n.len: track(tracked, n[i])
  1254. inc tracked.leftPartOfAsgn
  1255. of nkError:
  1256. localError(tracked.config, n.info, errorToString(tracked.config, n))
  1257. else:
  1258. for i in 0..<n.safeLen: track(tracked, n[i])
  1259. proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
  1260. if spec.typ.kind == tyOr:
  1261. result = false
  1262. for t in spec.typ:
  1263. if safeInheritanceDiff(g.excType(real), t) <= 0:
  1264. return true
  1265. else:
  1266. return safeInheritanceDiff(g.excType(real), spec.typ) <= 0
  1267. proc checkRaisesSpec(g: ModuleGraph; emitWarnings: bool; spec, real: PNode, msg: string, hints: bool;
  1268. effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.};
  1269. hintsArg: PNode = nil; isForbids: bool = false) =
  1270. # check that any real exception is listed in 'spec'; mark those as used;
  1271. # report any unused exception
  1272. var used = initIntSet()
  1273. for r in items(real):
  1274. block search:
  1275. for s in 0..<spec.len:
  1276. if effectPredicate(g, spec[s], r):
  1277. if isForbids: break
  1278. used.incl(s)
  1279. break search
  1280. if isForbids:
  1281. break search
  1282. # XXX call graph analysis would be nice here!
  1283. pushInfoContext(g.config, spec.info)
  1284. var rr = if r.kind == nkRaiseStmt: r[0] else: r
  1285. while rr.kind in {nkStmtList, nkStmtListExpr} and rr.len > 0: rr = rr.lastSon
  1286. message(g.config, r.info, if emitWarnings: warnEffect else: errGenerated,
  1287. renderTree(rr) & " " & msg & typeToString(r.typ))
  1288. popInfoContext(g.config)
  1289. # hint about unnecessarily listed exception types:
  1290. if hints:
  1291. for s in 0..<spec.len:
  1292. if not used.contains(s):
  1293. message(g.config, spec[s].info, hintXCannotRaiseY,
  1294. "'$1' cannot raise '$2'" % [renderTree(hintsArg), renderTree(spec[s])])
  1295. proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) =
  1296. ## checks for consistent effects for multi methods.
  1297. let actual = branch.typ.n[0]
  1298. if actual.len != effectListLen: return
  1299. let p = disp.ast[pragmasPos]
  1300. let raisesSpec = effectSpec(p, wRaises)
  1301. if not isNil(raisesSpec):
  1302. checkRaisesSpec(g, false, raisesSpec, actual[exceptionEffects],
  1303. "can raise an unlisted exception: ", hints=off, subtypeRelation)
  1304. let tagsSpec = effectSpec(p, wTags)
  1305. if not isNil(tagsSpec):
  1306. checkRaisesSpec(g, false, tagsSpec, actual[tagEffects],
  1307. "can have an unlisted effect: ", hints=off, subtypeRelation)
  1308. let forbidsSpec = effectSpec(p, wForbids)
  1309. if not isNil(forbidsSpec):
  1310. checkRaisesSpec(g, false, forbidsSpec, actual[tagEffects],
  1311. "has an illegal effect: ", hints=off, subtypeRelation, isForbids=true)
  1312. if sfThread in disp.flags and notGcSafe(branch.typ):
  1313. localError(g.config, branch.info, "base method is GC-safe, but '$1' is not" %
  1314. branch.name.s)
  1315. when defined(drnim):
  1316. if not g.compatibleProps(g, disp.typ, branch.typ):
  1317. localError(g.config, branch.info, "for method '" & branch.name.s &
  1318. "' the `.requires` or `.ensures` properties are incompatible.")
  1319. proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode; s: PSym = nil) =
  1320. var effects = t.n[0]
  1321. if t.kind != tyProc or effects.kind != nkEffectList: return
  1322. if n.kind != nkEmpty:
  1323. internalAssert g.config, effects.len == 0
  1324. newSeq(effects.sons, effectListLen)
  1325. let raisesSpec = effectSpec(n, wRaises)
  1326. if not isNil(raisesSpec):
  1327. effects[exceptionEffects] = raisesSpec
  1328. elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):
  1329. effects[exceptionEffects] = newNodeI(nkArgList, effects.info)
  1330. let tagsSpec = effectSpec(n, wTags)
  1331. if not isNil(tagsSpec):
  1332. effects[tagEffects] = tagsSpec
  1333. elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):
  1334. effects[tagEffects] = newNodeI(nkArgList, effects.info)
  1335. let forbidsSpec = effectSpec(n, wForbids)
  1336. if not isNil(forbidsSpec):
  1337. effects[forbiddenEffects] = forbidsSpec
  1338. elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):
  1339. effects[forbiddenEffects] = newNodeI(nkArgList, effects.info)
  1340. let requiresSpec = propSpec(n, wRequires)
  1341. if not isNil(requiresSpec):
  1342. effects[requiresEffects] = requiresSpec
  1343. let ensuresSpec = propSpec(n, wEnsures)
  1344. if not isNil(ensuresSpec):
  1345. effects[ensuresEffects] = ensuresSpec
  1346. effects[pragmasEffects] = n
  1347. if s != nil and s.magic != mNone:
  1348. if s.magic != mEcho:
  1349. t.flags.incl tfNoSideEffect
  1350. proc rawInitEffects(g: ModuleGraph; effects: PNode) =
  1351. newSeq(effects.sons, effectListLen)
  1352. effects[exceptionEffects] = newNodeI(nkArgList, effects.info)
  1353. effects[tagEffects] = newNodeI(nkArgList, effects.info)
  1354. effects[forbiddenEffects] = newNodeI(nkArgList, effects.info)
  1355. effects[requiresEffects] = g.emptyNode
  1356. effects[ensuresEffects] = g.emptyNode
  1357. effects[pragmasEffects] = g.emptyNode
  1358. proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; c: PContext): TEffects =
  1359. rawInitEffects(g, effects)
  1360. result = TEffects(exc: effects[exceptionEffects], tags: effects[tagEffects],
  1361. forbids: effects[forbiddenEffects], owner: s, ownerModule: s.getModule,
  1362. init: @[], locked: @[], graph: g, config: g.config, c: c,
  1363. currentBlock: 1
  1364. )
  1365. result.guards.s = @[]
  1366. result.guards.g = g
  1367. when defined(drnim):
  1368. result.currOptions = g.config.options + s.options - {optStaticBoundsCheck}
  1369. else:
  1370. result.currOptions = g.config.options + s.options
  1371. result.guards.beSmart = optStaticBoundsCheck in result.currOptions
  1372. proc hasRealBody(s: PSym): bool =
  1373. ## also handles importc procs with runnableExamples, which requires `=`,
  1374. ## which is not a real implementation, refs #14314
  1375. result = {sfForward, sfImportc} * s.flags == {}
  1376. proc trackProc*(c: PContext; s: PSym, body: PNode) =
  1377. let g = c.graph
  1378. when defined(nimsuggest):
  1379. if g.config.expandDone():
  1380. return
  1381. var effects = s.typ.n[0]
  1382. if effects.kind != nkEffectList: return
  1383. # effects already computed?
  1384. if not s.hasRealBody: return
  1385. let emitWarnings = tfEffectSystemWorkaround in s.typ.flags
  1386. if effects.len == effectListLen and not emitWarnings: return
  1387. var inferredEffects = newNodeI(nkEffectList, s.info)
  1388. var t: TEffects = initEffects(g, inferredEffects, s, c)
  1389. rawInitEffects g, effects
  1390. if not isEmptyType(s.typ[0]) and
  1391. s.kind in {skProc, skFunc, skConverter, skMethod}:
  1392. var res = s.ast[resultPos].sym # get result symbol
  1393. t.scopes[res.id] = t.currentBlock
  1394. track(t, body)
  1395. if s.kind != skMacro:
  1396. let params = s.typ.n
  1397. for i in 1..<params.len:
  1398. let param = params[i].sym
  1399. let typ = param.typ
  1400. if isSinkTypeForParam(typ) or
  1401. (t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
  1402. (isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
  1403. createTypeBoundOps(t, typ, param.info)
  1404. if isOutParam(typ) and param.id notin t.init:
  1405. message(g.config, param.info, warnProveInit, param.name.s)
  1406. if not isEmptyType(s.typ[0]) and
  1407. (s.typ[0].requiresInit or s.typ[0].skipTypes(abstractInst).kind == tyVar or
  1408. strictDefs in c.features) and
  1409. s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone:
  1410. var res = s.ast[resultPos].sym # get result symbol
  1411. if res.id notin t.init:
  1412. if tfRequiresInit in s.typ[0].flags:
  1413. localError(g.config, body.info, "'$1' requires explicit initialization" % "result")
  1414. else:
  1415. message(g.config, body.info, warnProveInit, "result")
  1416. let p = s.ast[pragmasPos]
  1417. let raisesSpec = effectSpec(p, wRaises)
  1418. if not isNil(raisesSpec):
  1419. let useWarning = s.name.s == "=destroy"
  1420. checkRaisesSpec(g, useWarning, raisesSpec, t.exc, "can raise an unlisted exception: ",
  1421. hints=on, subtypeRelation, hintsArg=s.ast[0])
  1422. # after the check, use the formal spec:
  1423. effects[exceptionEffects] = raisesSpec
  1424. else:
  1425. effects[exceptionEffects] = t.exc
  1426. let tagsSpec = effectSpec(p, wTags)
  1427. if not isNil(tagsSpec):
  1428. checkRaisesSpec(g, false, tagsSpec, t.tags, "can have an unlisted effect: ",
  1429. hints=off, subtypeRelation)
  1430. # after the check, use the formal spec:
  1431. effects[tagEffects] = tagsSpec
  1432. else:
  1433. effects[tagEffects] = t.tags
  1434. let forbidsSpec = effectSpec(p, wForbids)
  1435. if not isNil(forbidsSpec):
  1436. checkRaisesSpec(g, false, forbidsSpec, t.tags, "has an illegal effect: ",
  1437. hints=off, subtypeRelation, isForbids=true)
  1438. # after the check, use the formal spec:
  1439. effects[forbiddenEffects] = forbidsSpec
  1440. else:
  1441. effects[forbiddenEffects] = t.forbids
  1442. let requiresSpec = propSpec(p, wRequires)
  1443. if not isNil(requiresSpec):
  1444. effects[requiresEffects] = requiresSpec
  1445. let ensuresSpec = propSpec(p, wEnsures)
  1446. if not isNil(ensuresSpec):
  1447. patchResult(t, ensuresSpec)
  1448. effects[ensuresEffects] = ensuresSpec
  1449. var mutationInfo = MutationInfo()
  1450. if views in c.features:
  1451. var partitions = computeGraphPartitions(s, body, g, {borrowChecking})
  1452. checkBorrowedLocations(partitions, body, g.config)
  1453. if sfThread in s.flags and t.gcUnsafe:
  1454. if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions:
  1455. #localError(s.info, "'$1' is not GC-safe" % s.name.s)
  1456. listGcUnsafety(s, onlyWarning=false, g.config)
  1457. else:
  1458. listGcUnsafety(s, onlyWarning=true, g.config)
  1459. #localError(s.info, warnGcUnsafe2, s.name.s)
  1460. if sfNoSideEffect in s.flags and t.hasSideEffect:
  1461. when false:
  1462. listGcUnsafety(s, onlyWarning=false, g.config)
  1463. else:
  1464. if c.compilesContextId == 0: # don't render extended diagnostic messages in `system.compiles` context
  1465. var msg = ""
  1466. listSideEffects(msg, s, g.config, t.c)
  1467. message(g.config, s.info, errGenerated, msg)
  1468. else:
  1469. localError(g.config, s.info, "") # simple error for `system.compiles` context
  1470. if not t.gcUnsafe:
  1471. s.typ.flags.incl tfGcSafe
  1472. if not t.hasSideEffect and sfSideEffect notin s.flags:
  1473. s.typ.flags.incl tfNoSideEffect
  1474. when defined(drnim):
  1475. if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, s, body)
  1476. when defined(useDfa):
  1477. if s.name.s == "testp":
  1478. dataflowAnalysis(s, body)
  1479. when false: trackWrites(s, body)
  1480. if strictNotNil in c.features and s.kind == skProc:
  1481. checkNil(s, body, g.config, c.idgen)
  1482. proc trackStmt*(c: PContext; module: PSym; n: PNode, isTopLevel: bool) =
  1483. if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
  1484. nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
  1485. return
  1486. let g = c.graph
  1487. var effects = newNodeI(nkEffectList, n.info)
  1488. var t: TEffects = initEffects(g, effects, module, c)
  1489. t.isTopLevel = isTopLevel
  1490. track(t, n)
  1491. when defined(drnim):
  1492. if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, module, n)