summary refs log tree commit diff stats
path: root/doc/destructors.md
Commit message (Expand)AuthorAgeFilesLines
* destructors: better docs [backport:2.0] (#22391)Andreas Rumpf2023-08-051-28/+57
* fixes #22174; fixes destructor examples (#22349)ringabout2023-07-311-6/+6
* destructors: update, =destroy does not require a 'var T' (#22168)Andreas Rumpf2023-06-271-3/+3
* allow destructors to accept non var parameters; deprecate `proc =destroy(x: v...ringabout2023-06-211-6/+6
* Fix indentation in destructors.md (#22048)Antonis Geralis2023-06-081-1/+1
* update the documentation of the dup hook (#21984)ringabout2023-06-031-2/+12
* adds documentation for `=wasMoved` and `=dup` hooks and small fixes (#21827)ringabout2023-05-111-1/+36
* fixes #21245; warn about destructors that can raise (#21726)Andreas Rumpf2023-04-261-3/+11
* Markdown indented code blocks (#20473)Andrey Makarov2022-10-051-35/+34
* Markdown links migration part 1 (#20319)Andrey Makarov2022-09-091-1/+2
* Markdown code blocks part 3 (#20117)Andrey Makarov2022-08-041-57/+46
* Improve Markdown code blocks & start moving docs to Markdown style (#19954)Andrey Makarov2022-07-151-0/+730
igEpsilon@users.noreply.github.com> 2017-10-28 11:33:35 +0200 committer Andreas Rumpf <rumpf_a@web.de> 2017-10-28 11:33:35 +0200 Add sections (type, var, let, const, using) support for reorder pragma (#6326)' href='/ahoang/Nim/commit/compiler/reorder.nim?h=devel&id=e2af486434c7ff401c412d890b490b4ac11ab321'>e2af48643 ^
f4553af34 ^
e2af48643 ^


f4553af34 ^
e2af48643 ^


73e661d01 ^




f4553af34 ^
e2af48643 ^
ce341982a ^
40ec7be45 ^
ce341982a ^
7e747d11c ^
cce1b24b1 ^

40ec7be45 ^
ce341982a ^
40ec7be45 ^
ce341982a ^
40ec7be45 ^

ce341982a ^

f4553af34 ^
e2af48643 ^
ce341982a ^

f4553af34 ^
e2af48643 ^
ce341982a ^
40ec7be45 ^
e2af48643 ^
f4553af34 ^
e2af48643 ^

40ec7be45 ^
ce341982a ^

40ec7be45 ^

ce341982a ^
40ec7be45 ^
ce341982a ^
e2af48643 ^
ce341982a ^

dfe067a74 ^
ce341982a ^

7e747d11c ^

ce341982a ^



e2af48643 ^









ce341982a ^

40ec7be45 ^
ce341982a ^
7e747d11c ^
e2af48643 ^
7e747d11c ^
e2af48643 ^
7e747d11c ^
336f1e63d ^


5b20f0685 ^
e2af48643 ^
7e747d11c ^
2f213db7e ^
ce341982a ^
7366a3da3 ^
7e747d11c ^
ce341982a ^
73e661d01 ^
93ced3135 ^
e2af48643 ^



7e747d11c ^
826c1e2d7 ^
e2af48643 ^
33b69f0ed ^
e2af48643 ^
33b69f0ed ^
826c1e2d7 ^
e2af48643 ^






7e747d11c ^
c94647aec ^
33b69f0ed ^
bf6c2c5cc ^
993b3909a ^
e2af48643 ^
826c1e2d7 ^
33b69f0ed ^
826c1e2d7 ^
33b69f0ed ^
e2af48643 ^




























5bf31fcab ^
e2af48643 ^















7e747d11c ^
e2af48643 ^

336f1e63d ^



26a6ceb34 ^
f4553af34 ^
336f1e63d ^























e2af48643 ^

336f1e63d ^



e2af48643 ^





73e661d01 ^
e2af48643 ^
73e661d01 ^
e2af48643 ^



















d677ed31e ^
b7116a28e ^


e2af48643 ^








93ced3135 ^
e2af48643 ^





5a26c3799 ^
e2af48643 ^




5a26c3799 ^
e2af48643 ^

93ced3135 ^
e2af48643 ^
336f1e63d ^
e2af48643 ^














5a26c3799 ^
e2af48643 ^

93ced3135 ^
e2af48643 ^











93ced3135 ^
e2af48643 ^



c10f84b9d ^







e2af48643 ^



7e747d11c ^
e2af48643 ^











f4553af34 ^
e2af48643 ^




f4553af34 ^
e2af48643 ^






f4553af34 ^
e2af48643 ^




f4553af34 ^
e2af48643 ^


c10f84b9d ^






e2af48643 ^
ce341982a ^

e2af48643 ^
f4553af34 ^
e2af48643 ^



























93ced3135 ^
9b378296f ^
e2af48643 ^




826c1e2d7 ^
e2af48643 ^
bf6c2c5cc ^
33b69f0ed ^
826c1e2d7 ^
ce341982a ^




826c1e2d7 ^
ce341982a ^
e2af48643 ^

5bf31fcab ^
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
 
      
                        
                                                     
           
 

                                         


                                   


                              






                           
                                  


                        
                              


                                           




                                              
                                
                      
 
                                                     
             
                     

                                    
                              
 
                                                                 
             

                                                 

                            
                                  
                                     

                               
                                  
                                           
                 
                               
                      
                                  

                         
                                  

               

                                                                                           
                    
                                            
             
                                         

                                   
                                             

                                             

                                            



                                   









                                                                        

                                   
                                                  
                                        
                              
                                                                                
                                                                          
              
                


                                                                                       
                                        
         
                                        
                                     
       
                                                                               
                                      
 
                                  
                



                               
                                                                             
                                                                 
                          
                                                    
 
                                                               
                                                                           






                                                        
                                                   
                               
                                                  
                                                                           
                                             
               
                                                    
                                                                   
                                                    
                                      




























                                                               
                                                                        















                                                                             
                            

                  



                                                                                               
                                        
                                      























                                                                  

                                   



                               





                                                  
                 
                                                                                
                  



















                                                                  
                  


                      








                                    
                





                           
                                                               




                                                         
                      

                                                                                
                  
               
                            














                                                         
                      

                                                                                
                  











                                       
                



                      







                                                          



                                                              
                               











                                               
                                      




                                                                         
                                      






                                                                                             
                                      




                                                                                                     
                                        


                                                                                             






                                                            
           

                              
                          
                                          



























                                                                                      
              
                        




                                      
                                                                  
                                  
                                                      
                                                 
                                                      




                                            
                                                                
 

                                    
                                            
import
  ast, idents, renderer,
  msgs, modulegraphs, syntaxes, options, modulepaths,
  lineinfos

import std/[algorithm, strutils, intsets]

when defined(nimPreviewSlimSystem):
  import std/assertions

when defined(nimDebugReorder):
  import std/tables

type
  DepN = ref object
    pnode: PNode
    id, idx, lowLink: int
    onStack: bool
    kids: seq[DepN]
    hAQ, hIS, hB, hCmd: int
    when defined(nimDebugReorder):
      expls: seq[string]
  DepG = seq[DepN]

when defined(nimDebugReorder):
  var idNames = newTable[int, string]()

proc newDepN(id: int, pnode: PNode): DepN =
  result = DepN(id: id, pnode: pnode, idx: -1,
                lowLink: -1, onStack: false,
                kids: @[], hAQ: -1, hIS: -1,
                hB: -1, hCmd: -1
  )
  when defined(nimDebugReorder):
    result.expls = @[]

proc accQuoted(cache: IdentCache; n: PNode): PIdent =
  var id = ""
  for i in 0..<n.len:
    let ident = n[i].getPIdent
    if ident != nil: id.add(ident.s)
  result = getIdent(cache, id)

proc addDecl(cache: IdentCache; n: PNode; declares: var IntSet) =
  case n.kind
  of nkPostfix: addDecl(cache, n[1], declares)
  of nkPragmaExpr: addDecl(cache, n[0], declares)
  of nkIdent:
    declares.incl n.ident.id
    when defined(nimDebugReorder):
      idNames[n.ident.id] = n.ident.s
  of nkSym:
    declares.incl n.sym.name.id
    when defined(nimDebugReorder):
      idNames[n.sym.name.id] = n.sym.name.s
  of nkAccQuoted:
    let a = accQuoted(cache, n)
    declares.incl a.id
    when defined(nimDebugReorder):
      idNames[a.id] = a.s
  of nkEnumFieldDef:
    addDecl(cache, n[0], declares)
  else: discard

proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLevel: bool) =
  template deps(n) = computeDeps(cache, n, declares, uses, false)
  template decl(n) =
    if topLevel: addDecl(cache, n, declares)
  case n.kind
  of procDefs, nkMacroDef, nkTemplateDef:
    decl(n[0])
    for i in 1..bodyPos: deps(n[i])
  of nkLetSection, nkVarSection, nkUsingStmt:
    for a in n:
      if a.kind in {nkIdentDefs, nkVarTuple}:
        for j in 0..<a.len-2: decl(a[j])
        for j in a.len-2..<a.len: deps(a[j])
  of nkConstSection, nkTypeSection:
    for a in n:
      if a.len >= 3:
        decl(a[0])
        for i in 1..<a.len:
          if a[i].kind == nkEnumTy:
            # declare enum members
            for b in a[i]:
              decl(b)
          else:
            deps(a[i])
  of nkIdentDefs:
    for i in 1..<n.len: # avoid members identifiers in object definition
      deps(n[i])
  of nkIdent: uses.incl n.ident.id
  of nkSym: uses.incl n.sym.name.id
  of nkAccQuoted: uses.incl accQuoted(cache, n).id
  of nkOpenSymChoice, nkClosedSymChoice:
    uses.incl n[0].sym.name.id
  of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
    for i in 0..<n.len: computeDeps(cache, n[i], declares, uses, topLevel)
  of nkPragma:
    let a = n[0]
    if a.kind == nkExprColonExpr and a[0].kind == nkIdent and a[0].ident.s == "pragma":
      # user defined pragma
      decl(a[1])
      for i in 1..<n.safeLen: deps(n[i])
    else:
      for i in 0..<n.safeLen: deps(n[i])
  of nkMixinStmt, nkBindStmt: discard
  else:
    # XXX: for callables, this technically adds the return type dep before args
    for i in 0..<n.safeLen: deps(n[i])

proc hasIncludes(n: PNode): bool =
  result = false
  for a in n:
    if a.kind == nkIncludeStmt:
      return true

proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode =
  result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
  graph.addDep(s, fileIdx)
  graph.addIncludeDep(FileIndex s.position, fileIdx)

proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
                    modulePath: string, includedFiles: var IntSet): PNode =
  # Parses includes and injects them in the current tree
  if not n.hasIncludes:
    return n
  result = newNodeI(nkStmtList, n.info)
  for a in n:
    if a.kind == nkIncludeStmt:
      for i in 0..<a.len:
        var f = checkModuleName(graph.config, a[i])
        if f != InvalidFileIdx:
          if containsOrIncl(includedFiles, f.int):
            localError(graph.config, a.info, "recursive dependency: '$1'" %
              toMsgFilename(graph.config, f))
          else:
            let nn = includeModule(graph, module, f)
            let nnn = expandIncludes(graph, module, nn, modulePath,
                                      includedFiles)
            excl(includedFiles, f.int)
            for b in nnn:
              result.add b
    else:
      result.add a

proc splitSections(n: PNode): PNode =
  # Split typeSections and ConstSections into
  # sections that contain only one definition
  assert n.kind == nkStmtList
  result = newNodeI(nkStmtList, n.info)
  for a in n:
    if a.kind in {nkTypeSection, nkConstSection} and a.len > 1:
      for b in a:
        var s = newNode(a.kind)
        s.info = b.info
        s.add b
        result.add s
    else:
      result.add a

proc haveSameKind(dns: seq[DepN]): bool =
  # Check if all the nodes in a strongly connected
  # component have the same kind
  result = true
  let kind = dns[0].pnode.kind
  for dn in dns:
    if dn.pnode.kind != kind:
      return false

proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) =
  # Merges typeSections and ConstSections when they form
  # a strong component (ex: circular type definition)
  for c in comps:
    assert c.len > 0
    if c.len == 1:
      res.add c[0].pnode
    else:
      let fstn = c[0].pnode
      let kind = fstn.kind
      # always return to the original order when we got circular dependencies
      let cs = c.sortedByIt(it.id)
      if kind in {nkTypeSection, nkConstSection} and haveSameKind(cs):
        # Circular dependency between type or const sections, we just
        # need to merge them
        var sn = newNode(kind)
        for dn in cs:
          sn.add dn.pnode[0]
        res.add sn
      else:
        # Problematic circular dependency, we arrange the nodes into
        # their original relative order and make sure to re-merge
        # consecutive type and const sections
        var wmsg = "Circular dependency detected. `codeReordering` pragma may not be able to" &
          " reorder some nodes properly"
        when defined(nimDebugReorder):
          wmsg &= ":\n"
          for i in 0..<cs.len-1:
            for j in i..<cs.len:
              for ci in 0..<cs[i].kids.len:
                if cs[i].kids[ci].id == cs[j].id:
                  wmsg &= "line " & $cs[i].pnode.info.line &
                    " depends on line " & $cs[j].pnode.info.line &
                    ": " & cs[i].expls[ci] & "\n"
          for j in 0..<cs.len-1:
            for ci in 0..<cs[^1].kids.len:
              if cs[^1].kids[ci].id == cs[j].id:
                wmsg &= "line " & $cs[^1].pnode.info.line &
                  " depends on line " & $cs[j].pnode.info.line &
                  ": " & cs[^1].expls[ci] & "\n"
        message(conf, cs[0].pnode.info, warnUser, wmsg)

        var i = 0
        while i < cs.len:
          if cs[i].pnode.kind in {nkTypeSection, nkConstSection}:
            let ckind = cs[i].pnode.kind
            var sn = newNode(ckind)
            sn.add cs[i].pnode[0]
            inc i
            while i < cs.len and cs[i].pnode.kind == ckind:
              sn.add cs[i].pnode[0]
              inc i
            res.add sn
          else:
            res.add cs[i].pnode
            inc i

proc hasImportStmt(n: PNode): bool =
  # Checks if the node is an import statement or
  # i it contains one
  case n.kind
  of nkImportStmt, nkFromStmt, nkImportExceptStmt:
    result = true
  of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
    result = false
    for a in n:
      if a.hasImportStmt:
        return true
  else:
    result = false

proc hasImportStmt(n: DepN): bool =
  if n.hIS < 0:
    n.hIS = ord(n.pnode.hasImportStmt)
  result = bool(n.hIS)

proc hasCommand(n: PNode): bool =
  # Checks if the node is a command or a call
  # or if it contains one
  case n.kind
  of nkCommand, nkCall:
    result = true
  of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse,
      nkStaticStmt, nkLetSection, nkConstSection, nkVarSection,
      nkIdentDefs:
    result = false
    for a in n:
      if a.hasCommand:
        return true
  else:
    return false

proc hasCommand(n: DepN): bool =
  if n.hCmd < 0:
    n.hCmd = ord(n.pnode.hasCommand)
  result = bool(n.hCmd)

proc hasAccQuoted(n: PNode): bool =
  result = false
  if n.kind == nkAccQuoted:
    return true
  for a in n:
    if hasAccQuoted(a):
      return true

const extendedProcDefs = procDefs + {nkMacroDef, nkTemplateDef}

proc hasAccQuotedDef(n: PNode): bool =
  # Checks if the node is a function, macro, template ...
  # with a quoted name or if it contains one
  case n.kind
  of extendedProcDefs:
    result = n[0].hasAccQuoted
  of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
    result = false
    for a in n:
      if hasAccQuotedDef(a):
        return true
  else:
    result = false

proc hasAccQuotedDef(n: DepN): bool =
  if n.hAQ < 0:
    n.hAQ = ord(n.pnode.hasAccQuotedDef)
  result = bool(n.hAQ)

proc hasBody(n: PNode): bool =
  # Checks if the node is a function, macro, template ...
  # with a body or if it contains one
  case n.kind
  of nkCommand, nkCall:
    result = true
  of extendedProcDefs:
    result = n[^1].kind == nkStmtList
  of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
    result = false
    for a in n:
      if a.hasBody:
        return true
  else:
    result = false

proc hasBody(n: DepN): bool =
  if n.hB < 0:
    n.hB = ord(n.pnode.hasBody)
  result = bool(n.hB)

proc intersects(s1, s2: IntSet): bool =
  result = false
  for a in s1:
    if s2.contains(a):
      return true

proc hasPushOrPopPragma(n: DepN): bool =
  # Checks if the tree node has some pragmas that do not
  # play well with reordering, like the push/pop pragma
  # no crossing for push/pop barrier
  let a = n.pnode
  result = a.kind == nkPragma and a[0].kind == nkIdent and
      (a[0].ident.s == "push" or a[0].ident.s == "pop")

proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
  # Build a dependency graph
  result = newSeqOfCap[DepN](deps.len)
  for i in 0..<deps.len:
    result.add newDepN(i, n[i])
  for i in 0..<deps.len:
    var ni = result[i]
    let uses = deps[i][1]
    let niHasBody = ni.hasBody
    let niHasCmd = ni.hasCommand
    for j in 0..<deps.len:
      if i == j: continue
      var nj = result[j]
      let declares = deps[j][0]
      if j < i and nj.hasCommand and niHasCmd:
        # Preserve order for commands and calls
        ni.kids.add nj
        when defined(nimDebugReorder):
          ni.expls.add "both have commands and one comes after the other"
      elif j < i and nj.hasImportStmt:
        # Every node that comes after an import statement must
        # depend on that import
        ni.kids.add nj
        when defined(nimDebugReorder):
          ni.expls.add "parent is, or contains, an import statement and child comes after it"
      elif j < i and niHasBody and nj.hasAccQuotedDef:
        # Every function, macro, template... with a body depends
        # on precedent function declarations that have quoted names.
        # That's because it is hard to detect the use of functions
        # like "[]=", "[]", "or" ... in their bodies.
        ni.kids.add nj
        when defined(nimDebugReorder):
          ni.expls.add "one declares a quoted identifier and the other has a body and comes after it"
      elif j < i and niHasBody and not nj.hasBody and
        intersects(deps[i][0], declares):
          # Keep function declaration before function definition
          ni.kids.add nj
          when defined(nimDebugReorder):
            for dep in deps[i][0]:
              if dep in declares:
                ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it"
      elif hasPushOrPopPragma(nj):
        # Every node that comes after a push/pop pragma must
        # depend on it; vice versa
        if j < i:
          ni.kids.add nj
        else:
          nj.kids.add ni
      else:
        for d in declares:
          if uses.contains(d):
            ni.kids.add nj
            when defined(nimDebugReorder):
              ni.expls.add "one declares \"" & idNames[d] & "\" and the other uses it"

proc strongConnect(v: var DepN, idx: var int, s: var seq[DepN],
                   res: var seq[seq[DepN]]) =
  # Recursive part of trajan's algorithm
  v.idx = idx
  v.lowLink = idx
  inc idx
  s.add v
  v.onStack = true
  for w in v.kids.mitems:
    if w.idx < 0:
      strongConnect(w, idx, s, res)
      v.lowLink = min(v.lowLink, w.lowLink)
    elif w.onStack:
      v.lowLink = min(v.lowLink, w.idx)
  if v.lowLink == v.idx:
    var comp = newSeq[DepN]()
    while true:
      var w = s.pop
      w.onStack = false
      comp.add w
      if w.id == v.id: break
    res.add comp

proc getStrongComponents(g: var DepG): seq[seq[DepN]] =
  ## Tarjan's algorithm. Performs a topological sort
  ## and detects strongly connected components.
  result = @[]
  var s: seq[DepN] = @[]
  var idx = 0
  for v in g.mitems:
    if v.idx < 0:
      strongConnect(v, idx, s, result)

proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode =
  var includedFiles = initIntSet()
  let mpath = toFullPath(graph.config, module.fileIdx)
  let n = expandIncludes(graph, module, n, mpath,
                          includedFiles).splitSections
  result = newNodeI(nkStmtList, n.info)
  var deps = newSeq[(IntSet, IntSet)](n.len)
  for i in 0..<n.len:
    deps[i][0] = initIntSet()
    deps[i][1] = initIntSet()
    computeDeps(graph.cache, n[i], deps[i][0], deps[i][1], true)

  var g = buildGraph(n, deps)
  let comps = getStrongComponents(g)
  mergeSections(graph.config, comps, result)