context.mli 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. (** Context analysis: find declarations of used variables and functions. *)
  2. (** {2 Overview }
  3. The desugared CiviC code contains [Var], [FunCall] and [Assign] nodes. These
  4. all use variables or functions identified by a [string] name. The context
  5. analysis phase links each variable occurrence to its declaration: a
  6. [VarDec], [Param], [Dim], [GlobalDe[cf]], [FunDe[cf]] or [For] node. Since
  7. the original nodes only have a [string] field to save the declaration, new
  8. node types have been added which replace the name with a declaration node:
  9. [VarUse], [FunUse], and [VarLet].
  10. Context analysis can be broken down into the following steps: {ol
  11. {li Find all function declarations in the current scope. }
  12. {li Traverse the current scope without traversing into nested functions,
  13. adding variable declarations as they are declared, and applying renaming
  14. rules as described below. Error messages for duplicate varaible
  15. declarations, undefined variable uses, and assignments to loop induction
  16. variables are generated here. }
  17. {li Traverse into (nested) functions and go to step 1, while copying the
  18. current scope for each function (so that the higher scope is not
  19. polluted with local variables of the nested function). }
  20. }
  21. {2 Renaming variables }
  22. One task of context analysis is resolvement of scoping conclicts.
  23. Specifically, when a function parameter or local variable shadows a variable
  24. in a higher scope (either an ancestor function or the global scope),
  25. initializations in the corresponding function that reference the shadowed
  26. variable will be invalidated when the initialization is moved down during
  27. desugaring. This fixed by first traversing into the initialization part of a
  28. variable declaration before adding the newly declared variable to the scope,
  29. and then renaming the declared variable and all of its future uses. An
  30. example follows:
  31. {v int a = 1;
  32. int foo() \{
  33. int a = a + 1; // 'a' is already in the scope, so rename it
  34. return a;
  35. \} v}
  36. becomes:
  37. {v int a = 1;
  38. int foo() \{
  39. int _a_1 = a + 1;
  40. return _a_1; // uses of local 'a' must be renamed as well
  41. \} v}
  42. Note that the renaming scheme also solves the case in which a variable with
  43. the name of another local variable that is declared later on, is referenced
  44. in an initialization expression:
  45. {v int a = 1;
  46. int foo() \{
  47. int b = a + 1; // should reference global 'a', not local 'a' below
  48. int a = 2; // 'a' is already in the scope, so rename it
  49. return a;
  50. \} v}
  51. After desugaring, this becomes:
  52. {v int a = 1;
  53. int foo() \{
  54. int b;
  55. int _a_1;
  56. b = a + 1; // 'a' correctly references to global variable
  57. _a_1 = 2;
  58. return _a_1;
  59. \} v}
  60. {3 For-loop counters }
  61. Induction variables in for-loop counters form a special case since they may
  62. shadow a local variable in the current scope, and even the induction
  63. variable of a parent for-loop. Since for-loops will be rewritten to
  64. while-loops during desugaring and these semantics only apply to for-loops,
  65. induction variables are renamed to fresh variables as needed to avoid
  66. scoping conflicts:
  67. {v
  68. void foo() \{ | void foo() \{
  69. int i; | int i;
  70. printInt(i); | printInt(i);
  71. for (int i = 1, 10) \{ | for (int _i_1 = 1, 10) \{
  72. printInt(i); | printInt(_i_1);
  73. for (int i = 1, 10) \{ | for (int _i_2 = 1, 10) \{
  74. printInt(i); | printInt(_i_2);
  75. \} | \}
  76. printInt(i); | printInt(_i_1);
  77. \} | \}
  78. printInt(i); | printInt(i);
  79. \} | \} v}
  80. {3 Imported array dimensions }
  81. Another special case are dimensions of imported arrays. In [extern int[n]
  82. a], [n] is just a name given locally to the first dimension of [a]. Since
  83. this name is unknown the module where the dimension is exported, this needs
  84. to be consistently transformed into a format which both modules can decide
  85. on independently.
  86. Our implementation changes the snippet into [extern int[__a_0] a], where the
  87. [__] prefix indicates an array dimension and [a_0] indicates the first
  88. dimension of [a]. The double underscore prefix is necessary to assert
  89. uniqueness of the variable, since the suffix is based on the dimension index
  90. rather than the global counter that is used for other generated variables
  91. (we cannot use this global counter since it may differ between compilation
  92. instances of the different modules).
  93. This way, exporting dimensions global arrays in another module can be done
  94. in the same way while asserting uniqeness of the imported/exported
  95. variables.
  96. For exported variables, this renaming is done in the {!Desug} phase. The
  97. reason for this is that no separate variable exist yet for exported array
  98. dimensions at this stage, they are generated during desugaring.
  99. *)
  100. (** Traversal that replaces names with declarations. Exported for use in other
  101. phases. The boolean argument toggles renaming, which is only [true] the
  102. first time the phase is run (after that it is not necessary since the
  103. compiler does not generate conflicting variables). *)
  104. val analyse : bool -> Types.node -> Types.node
  105. (** Main phase function, called by {!Main}. Calls {!analyse}. *)
  106. val phase : Main.phase_func