cursors.nim 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2019 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. import
  10. intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
  11. strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
  12. lineinfos, parampatterns
  13. ##[
  14. This module implements "cursor" detection. A cursor is a local variable
  15. that is used for navigation in a datastructure, it does not "own" the
  16. data it aliases but it might update the underlying datastructure.
  17. Two primary examples for cursors that I have in mind and that are critical
  18. for optimization:
  19. 1. Local string variable introduced by ``for x in a``::
  20. var i = 0
  21. while i < a.len:
  22. let cursor = a[i]
  23. use cursor
  24. inc i
  25. 2. Local ``ref`` variable for navigation::
  26. var cursor = listHead
  27. while cursor != nil:
  28. use cursor
  29. cursor = cursor.next
  30. Cursors are very interesting for the optimizer because they can be copyMem'ed
  31. and don't need a destructor.
  32. More formally, a cursor is a variable that is set on all paths to
  33. a *location* or a proc call that produced a ``lent/var`` type. All statements
  34. that come after these assignments MUST not mutate what the cursor aliases.
  35. Mutations *through* the cursor are allowed if the cursor has ref semantics.
  36. Look at this complex real world example taken from the compiler itself:
  37. .. code-block:: Nim
  38. proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
  39. var t = typ
  40. while true:
  41. if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
  42. return t.sym.loc.r
  43. if t.kind in irrelevantForBackend:
  44. t = t.lastSon
  45. else:
  46. break
  47. let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.lastSon else: typ
  48. if typ.loc.r == nil:
  49. typ.loc.r = typ.typeName & $sig
  50. result = typ.loc.r
  51. if result == nil: internalError(m.config, "getTypeName: " & $typ.kind)
  52. Here `t` is a cursor but without a control flow based analysis we are unlikely
  53. to detect it.
  54. ]##
  55. # Araq: I owe you an implementation. For now use the .cursor pragma. :-/