sempass2.nim 57 KB

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