sempass2.nim 63 KB

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