rubycc 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +56 -0
  3. data/LICENSE.txt +21 -0
  4. data/NOTICE +52 -0
  5. data/README.md +208 -0
  6. data/data/README.md +117 -0
  7. data/data/r10_corpus_scan.json +4082 -0
  8. data/data/r10_manual_classification.json +5342 -0
  9. data/data/r10_verification_m4a.json +1531 -0
  10. data/data/r10_verification_m4b.json +1818 -0
  11. data/data/r10_verification_m4c.json +1489 -0
  12. data/data/r10_verification_m4d.json +318 -0
  13. data/data/r10_verification_rbs.json +192 -0
  14. data/data/verified_gems.json +397 -0
  15. data/exe/rmake +16 -0
  16. data/exe/rubycc +9 -0
  17. data/exe/rubycc-ar +114 -0
  18. data/exe/rubycc-doctor +14 -0
  19. data/exe/rubycc-pkgconf +8 -0
  20. data/include/float.h +87 -0
  21. data/include/iso646.h +18 -0
  22. data/include/libc/alloca.h +18 -0
  23. data/include/libc/arpa/inet.h +61 -0
  24. data/include/libc/assert.h +43 -0
  25. data/include/libc/dirent.h +72 -0
  26. data/include/libc/dlfcn.h +58 -0
  27. data/include/libc/features.h +205 -0
  28. data/include/libc/glibc/aarch64/ctype.h +108 -0
  29. data/include/libc/glibc/aarch64/endian.h +54 -0
  30. data/include/libc/glibc/aarch64/errno.h +154 -0
  31. data/include/libc/glibc/aarch64/fcntl.h +144 -0
  32. data/include/libc/glibc/aarch64/inttypes.h +182 -0
  33. data/include/libc/glibc/aarch64/limits.h +63 -0
  34. data/include/libc/glibc/aarch64/pthread.h +131 -0
  35. data/include/libc/glibc/aarch64/setjmp.h +84 -0
  36. data/include/libc/glibc/aarch64/stdint.h +174 -0
  37. data/include/libc/glibc/aarch64/sys/epoll.h +88 -0
  38. data/include/libc/glibc/aarch64/sys/fcntl.h +19 -0
  39. data/include/libc/glibc/aarch64/sys/select.h +75 -0
  40. data/include/libc/glibc/aarch64/sys/stat.h +136 -0
  41. data/include/libc/glibc/aarch64/sys/syscall.h +167 -0
  42. data/include/libc/glibc/aarch64/sys/time.h +74 -0
  43. data/include/libc/glibc/aarch64/sys/types.h +127 -0
  44. data/include/libc/glibc/aarch64/time.h +113 -0
  45. data/include/libc/glibc/x86_64/ctype.h +108 -0
  46. data/include/libc/glibc/x86_64/endian.h +54 -0
  47. data/include/libc/glibc/x86_64/errno.h +154 -0
  48. data/include/libc/glibc/x86_64/fcntl.h +134 -0
  49. data/include/libc/glibc/x86_64/inttypes.h +182 -0
  50. data/include/libc/glibc/x86_64/limits.h +61 -0
  51. data/include/libc/glibc/x86_64/pthread.h +111 -0
  52. data/include/libc/glibc/x86_64/setjmp.h +82 -0
  53. data/include/libc/glibc/x86_64/stdint.h +163 -0
  54. data/include/libc/glibc/x86_64/sys/epoll.h +91 -0
  55. data/include/libc/glibc/x86_64/sys/fcntl.h +19 -0
  56. data/include/libc/glibc/x86_64/sys/select.h +75 -0
  57. data/include/libc/glibc/x86_64/sys/stat.h +130 -0
  58. data/include/libc/glibc/x86_64/sys/syscall.h +188 -0
  59. data/include/libc/glibc/x86_64/sys/time.h +74 -0
  60. data/include/libc/glibc/x86_64/sys/types.h +123 -0
  61. data/include/libc/glibc/x86_64/time.h +113 -0
  62. data/include/libc/grp.h +50 -0
  63. data/include/libc/langinfo.h +126 -0
  64. data/include/libc/link.h +18 -0
  65. data/include/libc/locale.h +83 -0
  66. data/include/libc/math.h +191 -0
  67. data/include/libc/netinet/in.h +126 -0
  68. data/include/libc/netinet/tcp.h +44 -0
  69. data/include/libc/poll.h +46 -0
  70. data/include/libc/pwd.h +58 -0
  71. data/include/libc/regex.h +53 -0
  72. data/include/libc/sched.h +35 -0
  73. data/include/libc/signal.h +204 -0
  74. data/include/libc/stdio.h +157 -0
  75. data/include/libc/stdlib.h +92 -0
  76. data/include/libc/string.h +74 -0
  77. data/include/libc/strings.h +25 -0
  78. data/include/libc/sys/cdefs.h +126 -0
  79. data/include/libc/sys/inotify.h +111 -0
  80. data/include/libc/sys/ioctl.h +36 -0
  81. data/include/libc/sys/mman.h +65 -0
  82. data/include/libc/sys/param.h +41 -0
  83. data/include/libc/sys/resource.h +109 -0
  84. data/include/libc/sys/socket.h +167 -0
  85. data/include/libc/sys/statfs.h +82 -0
  86. data/include/libc/sys/timerfd.h +55 -0
  87. data/include/libc/sys/uio.h +40 -0
  88. data/include/libc/sys/un.h +25 -0
  89. data/include/libc/sys/utsname.h +35 -0
  90. data/include/libc/sys/wait.h +135 -0
  91. data/include/libc/termios.h +179 -0
  92. data/include/libc/unistd.h +194 -0
  93. data/include/stdalign.h +16 -0
  94. data/include/stdarg.h +31 -0
  95. data/include/stdatomic.h +158 -0
  96. data/include/stdbool.h +15 -0
  97. data/include/stdckdint.h +28 -0
  98. data/include/stddef.h +60 -0
  99. data/include/stdnoreturn.h +18 -0
  100. data/include/x86intrin.h +16 -0
  101. data/lib/rubycc/backend/aarch64.rb +1724 -0
  102. data/lib/rubycc/backend/x86_64.rb +1369 -0
  103. data/lib/rubycc/compile_error.rb +36 -0
  104. data/lib/rubycc/compiler.rb +305 -0
  105. data/lib/rubycc/doctor/builder.rb +151 -0
  106. data/lib/rubycc/doctor/cli.rb +190 -0
  107. data/lib/rubycc/doctor/fetcher.rb +84 -0
  108. data/lib/rubycc/doctor/gemfile.rb +115 -0
  109. data/lib/rubycc/doctor/verified_gems.rb +114 -0
  110. data/lib/rubycc/doctor.rb +16 -0
  111. data/lib/rubycc/driver.rb +463 -0
  112. data/lib/rubycc/front/ast.rb +528 -0
  113. data/lib/rubycc/front/constant_evaluator.rb +631 -0
  114. data/lib/rubycc/front/initializer_resolver.rb +592 -0
  115. data/lib/rubycc/front/lexeme_reader.rb +460 -0
  116. data/lib/rubycc/front/lexer.rb +232 -0
  117. data/lib/rubycc/front/parser.rb +4122 -0
  118. data/lib/rubycc/front/token.rb +49 -0
  119. data/lib/rubycc/ir/call_convention.rb +486 -0
  120. data/lib/rubycc/ir/generator.rb +6036 -0
  121. data/lib/rubycc/ir/ir.rb +417 -0
  122. data/lib/rubycc/link/compat_runtime.rb +89 -0
  123. data/lib/rubycc/link/errors.rb +13 -0
  124. data/lib/rubycc/link/executable_linker.rb +405 -0
  125. data/lib/rubycc/link/library_resolver.rb +437 -0
  126. data/lib/rubycc/link/partial_linker.rb +546 -0
  127. data/lib/rubycc/link/shared_linker.rb +1732 -0
  128. data/lib/rubycc/mkmf_shim.rb +83 -0
  129. data/lib/rubycc/objfile/ar_archive.rb +437 -0
  130. data/lib/rubycc/objfile/elf_reader.rb +646 -0
  131. data/lib/rubycc/objfile/elf_writer.rb +891 -0
  132. data/lib/rubycc/objfile/relocatable_writer.rb +376 -0
  133. data/lib/rubycc/pkgconf/cli.rb +139 -0
  134. data/lib/rubycc/pkgconf/errors.rb +39 -0
  135. data/lib/rubycc/pkgconf/model.rb +28 -0
  136. data/lib/rubycc/pkgconf/parser.rb +109 -0
  137. data/lib/rubycc/pkgconf/pkgconf.rb +15 -0
  138. data/lib/rubycc/pkgconf/resolver.rb +70 -0
  139. data/lib/rubycc/pkgconf/search_path.rb +38 -0
  140. data/lib/rubycc/pkgconf/system_path_filter.rb +131 -0
  141. data/lib/rubycc/preprocess/constant_expression.rb +219 -0
  142. data/lib/rubycc/preprocess/glibc_version.rb +124 -0
  143. data/lib/rubycc/preprocess/pp_token.rb +62 -0
  144. data/lib/rubycc/preprocess/preprocessor.rb +2020 -0
  145. data/lib/rubycc/preprocess/scanner.rb +290 -0
  146. data/lib/rubycc/preprocess/token_converter.rb +157 -0
  147. data/lib/rubycc/rmake/cli.rb +143 -0
  148. data/lib/rubycc/rmake/errors.rb +71 -0
  149. data/lib/rubycc/rmake/executor.rb +818 -0
  150. data/lib/rubycc/rmake/expander.rb +251 -0
  151. data/lib/rubycc/rmake/makefile.rb +352 -0
  152. data/lib/rubycc/rmake/model.rb +137 -0
  153. data/lib/rubycc/rmake/parser.rb +226 -0
  154. data/lib/rubycc/rmake/rmake.rb +15 -0
  155. data/lib/rubycc/type.rb +1236 -0
  156. data/lib/rubycc/version.rb +5 -0
  157. data/lib/rubycc.rb +36 -0
  158. data/lib/rubygems_plugin.rb +102 -0
  159. metadata +219 -0
@@ -0,0 +1,528 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubycc
4
+ module Front
5
+ # Abstract syntax tree nodes for the C subset. Each node carries a
6
+ # representative token (`token`) for source-location diagnostics.
7
+ module AST
8
+ # Integer literal. `value` is a Ruby Integer and `type` its Rubycc::Type,
9
+ # fixed by the parser from the constant's base, suffix and magnitude
10
+ # (6.4.4.1) — int/long or their unsigned forms; a character constant is
11
+ # int.
12
+ IntLit = Data.define(:value, :token, :type)
13
+
14
+ # Floating-point literal. `value` is a Ruby Float and `type` its
15
+ # Rubycc::Type — Type::Float for an f/F-suffixed constant, Type::Double
16
+ # otherwise (a plain or l/L-suffixed one) — fixed by the parser from the
17
+ # constant's suffix (6.4.4.2).
18
+ FloatLit = Data.define(:value, :type, :token)
19
+
20
+ # String literal. `value` is the escape-resolved bytes as an ASCII-8BIT
21
+ # String, without the NUL terminator. Its type is char[N+1] (N being the
22
+ # byte count); in every expression context except sizeof it decays to a
23
+ # char * pointing at the string's first byte in .rodata.
24
+ StringLit = Data.define(:value, :token)
25
+
26
+ # Unary operation. `op` is :neg or :not (unary + is folded away by the
27
+ # parser), :addr (the address-of "&") or :deref (the dereference "*").
28
+ Unary = Data.define(:op, :operand, :token)
29
+
30
+ # Binary operation. `op` is one of :add, :sub, :mul, :div, :mod
31
+ # (arithmetic), :and, :or, :xor (bitwise), :shl, :shr (shifts) or
32
+ # :eq, :ne, :lt, :le, :gt, :ge (comparisons yielding int 0/1). The unary
33
+ # bitwise-not "~x" has no op of its own: the parser desugars it to the
34
+ # Binary "x ^ -1", so it reaches the generator as an ordinary :xor.
35
+ Binary = Data.define(:op, :lhs, :rhs, :token)
36
+
37
+ # The comma operator "left, right" (ISO C 6.5.17): `left` is evaluated
38
+ # for its side effects and its value discarded, then `right` is evaluated
39
+ # and its value and type become the whole expression's. Only a context
40
+ # that admits a full expression (a parenthesized expression, a subscript,
41
+ # an expression-statement, the three for-clauses, a control condition, a
42
+ # return, the middle of "?:") ever builds one; a comma that merely
43
+ # separates items (call arguments, declarators) is not this node.
44
+ Comma = Data.define(:left, :right, :token)
45
+
46
+ # Short-circuit "&&" and "||". Kept apart from Binary because both
47
+ # operands must be int-typed and only one side may end up evaluated at
48
+ # run time, unlike every other Binary operator.
49
+ LogicalAnd = Data.define(:lhs, :rhs, :token)
50
+ LogicalOr = Data.define(:lhs, :rhs, :token)
51
+
52
+ # The conditional operator "condition ? then_expr : else_expr".
53
+ # `condition` must be int-typed; `then_expr` and `else_expr` must share
54
+ # the same type (int/int or same-type pointer/pointer).
55
+ Conditional = Data.define(:condition, :then_expr, :else_expr, :token)
56
+
57
+ # Compound assignment "target op= value". `op` is one of :add, :sub,
58
+ # :mul, :div, :mod (the operator without its trailing "="). `target` is
59
+ # a VariableRef, a Subscript or a dereference (Unary with op :deref); the
60
+ # parser rejects any other, non-assignable target.
61
+ CompoundAssignment = Data.define(:op, :target, :value, :token)
62
+
63
+ # Prefix or postfix "++"/"--". `op` is :add for "++" and :sub for "--".
64
+ # `prefix` is true for "++x"/"--x" and false for "x++"/"x--". `target`
65
+ # is a VariableRef, a Subscript or a dereference, exactly like
66
+ # CompoundAssignment.
67
+ IncDec = Data.define(:op, :target, :prefix, :token)
68
+
69
+ # `return <expr>;`, or `return;` with `expr` nil (a void return).
70
+ Return = Data.define(:expr, :token)
71
+
72
+ # A local variable declaration. `type` is the declared Rubycc::Type
73
+ # (int or a pointer). `initializer` is an expression node or nil when the
74
+ # declarator has no "= <expr>" part. `const` is true when the object is
75
+ # top-level const-qualified (the only qualification M1 tracks), which the
76
+ # generator rejects writes against. `storage` records the storage-class
77
+ # specifier (nil, :static or :extern) for Phase B; the generator does not
78
+ # consume it yet. `alignas` is the boundary an _Alignas specifier asked for
79
+ # (6.7.5), or nil when the object takes its type's own alignment.
80
+ VariableDecl = Data.define(:name, :type, :initializer, :token, :const, :storage, :alignas)
81
+
82
+ # A file-scope (global) variable declaration. `type` is the declared
83
+ # Rubycc::Type (int, char, a pointer, a one-dimensional array or a
84
+ # struct/union). At most one of two initializer fields is set:
85
+ # `initializer_value` is a plain scalar-integer initializer folded to a
86
+ # Ruby Integer on the spot (the common "int g = 1 + 2;" case, kept folded
87
+ # so nothing downstream re-evaluates it), while `initializer_node` carries
88
+ # a still-unfolded initializer the generator lowers itself — a brace
89
+ # initializer-list (AST::InitializerList) for an aggregate or scalar, a
90
+ # string literal for a char array, or an address constant for a pointer
91
+ # (a null pointer, "&other_global", a decayed global array name or a
92
+ # string literal). Both are nil for an uninitialized global, which lands
93
+ # in .bss. `const` and `storage` carry the same top-level const flag and
94
+ # storage-class specifier (nil/:static/:extern) as VariableDecl; the
95
+ # generator diagnoses writes against a const global but does not yet act
96
+ # on the storage class (Phase B). `alignas` carries the same _Alignas
97
+ # boundary VariableDecl does.
98
+ GlobalDecl = Data.define(:name, :type, :initializer_value, :initializer_node, :token, :const, :storage,
99
+ :alignas)
100
+
101
+ # A brace-enclosed initializer "{ ... }" (ISO C 6.7.9). `items` is the
102
+ # ordered list of InitItem, one per comma-separated element (a trailing
103
+ # comma adds none). Appears as a VariableDecl/GlobalDecl initializer, or
104
+ # nested as an InitItem's value; the resolver walks it against the
105
+ # object's type to place each scalar. `token` is the opening brace.
106
+ InitializerList = Data.define(:items, :token)
107
+
108
+ # One element of an initializer-list. `designators` is the (possibly
109
+ # empty) leading designator chain — ArrayDesignator / MemberDesignator
110
+ # nodes, so ".a.b = x" and "[1].m = y" carry two — that redirects the
111
+ # current object before `value` initializes it; `value` is either an
112
+ # expression node or a nested InitializerList.
113
+ InitItem = Data.define(:designators, :value)
114
+
115
+ # An array designator "[constant-expression]" (6.7.9). `index` is the
116
+ # bracketed constant folded to a Ruby Integer at parse time; `token` is
117
+ # the "[" for diagnostics (an out-of-range index).
118
+ ArrayDesignator = Data.define(:index, :token)
119
+
120
+ # A member designator ".identifier" (6.7.9). `name` is the member's name;
121
+ # `token` is the "." for diagnostics (an unknown member).
122
+ MemberDesignator = Data.define(:name, :token)
123
+
124
+ # A reference to a local variable by name.
125
+ VariableRef = Data.define(:name, :token)
126
+
127
+ # A subscript `target[index]` (postfix "[" expression "]"). Both `target`
128
+ # and `index` are expression nodes; the generator lowers it as
129
+ # "*(target + index)" and type-checks that `target` is an array/pointer.
130
+ Subscript = Data.define(:target, :index, :token)
131
+
132
+ # A struct member access. `base` is the postfix-expression on the left,
133
+ # `member` the identifier String on the right and `arrow` false for the
134
+ # "." form (base is the struct itself) or true for the "->" form (base is
135
+ # a pointer to the struct, so "p->m" means "(*p).m"). Both forms are
136
+ # lvalues: the generator lowers either to the base struct's address plus
137
+ # the member's constant offset, then reads or writes through it.
138
+ MemberAccess = Data.define(:base, :member, :arrow, :token)
139
+
140
+ # `sizeof operand`: the byte size of a unary-expression's result type.
141
+ # `operand` is an expression node; the array-to-pointer decay does not
142
+ # apply to it, so `sizeof a` measures the whole array.
143
+ SizeofExpr = Data.define(:operand, :token)
144
+
145
+ # `sizeof ( type-name )`: the byte size of a written type ("int",
146
+ # "int *", ...). `type` is the resolved Rubycc::Type.
147
+ SizeofType = Data.define(:type, :token)
148
+
149
+ # `_Alignof ( type-name )` (ISO C 6.5.3.4): the alignment requirement, in
150
+ # bytes, of a written type. `type` is the resolved Rubycc::Type. Only the
151
+ # parenthesized type-name form exists; unlike sizeof there is no operand
152
+ # form. The result folds to a size_t (unsigned long) constant, exactly
153
+ # like SizeofType, and is rejected for a void, function or incomplete type.
154
+ AlignofType = Data.define(:type, :token)
155
+
156
+ # A cast "( type-name ) operand" (ISO C 6.5.4). `type` is the resolved
157
+ # Rubycc::Type the operand is converted to and `operand` is the
158
+ # cast-expression to its right. The generator settles which conversions
159
+ # are legal (arithmetic reinterpretation, a pointer retag, discarding a
160
+ # value with "(void)", a null-pointer-constant cast) and emits any code a
161
+ # narrowing needs; the parser only builds the node.
162
+ Cast = Data.define(:type, :operand, :token)
163
+
164
+ # A compound literal "( type-name ) { initializer-list }" (ISO C 6.5.2.5).
165
+ # `type` is the resolved Rubycc::Type of the unnamed object the literal
166
+ # denotes, with any inferred "[]" array bound already completed by the
167
+ # parser (so "(int[]){1,2,3}" carries int[3]); `initializer` is the
168
+ # AST::InitializerList that fills it. In block scope the object has
169
+ # automatic storage lasting the enclosing block, so the literal is an
170
+ # lvalue: the generator lays it out on a stack object, initializes it, and
171
+ # yields the object's value the same way a variable of `type` would (a
172
+ # struct/union as its base address, an array decayed to an element pointer,
173
+ # a scalar as a load) — which is what lets "&(T){...}", "(T){...}.member",
174
+ # a by-value argument and an assignment right-hand side all work. A
175
+ # compound literal in a static-storage-duration (file-scope) initializer is
176
+ # diagnosed as unsupported by the generator. `token` is the opening "(".
177
+ CompoundLiteral = Data.define(:type, :initializer, :token)
178
+
179
+ # A null pointer constant (ISO C11 6.3.2.3p3): "An integer constant
180
+ # expression with the value 0, or such an expression cast to type
181
+ # void *, is called a null pointer constant." #integer_constant_zero?
182
+ # below recognizes the first alternative on its own — that is exactly
183
+ # ConstantEvaluator's notion of an expression that folds to the integer
184
+ # 0, which already covers a plain integer literal 0, a character
185
+ # constant like '\0' (the lexer lowers it to an IntLit 0), an
186
+ # enumeration constant of value 0 (the parser folds a reference to one
187
+ # into an IntLit 0 on the spot, see #parse_primary_expression), a purely
188
+ # arithmetic fold such as "1 - 1", and — because 6.6p6 allows a cast to
189
+ # an integer type inside an integer constant expression — any of those
190
+ # cast to another integer type, such as "(unsigned long)0" or
191
+ # "(char)0". A cast to a *floating* type is never allowed in an integer
192
+ # constant expression (6.6p6 restricts a constant-expression cast to
193
+ # converting to an integer type, outside sizeof/alignof/_Alignof), so
194
+ # "(double)0" is correctly excluded here — matching the "invalid
195
+ # operands" gcc gives comparing it against a pointer, rather than the
196
+ # silent accept a floating cast would otherwise get. The second
197
+ # alternative — that whole expression additionally cast to "void *" — is
198
+ # the explicit Cast case below, since ConstantEvaluator itself only
199
+ # folds a cast to an *integer* destination.
200
+ #
201
+ # A null pointer constant converts implicitly to any pointer type in an
202
+ # assignment, an initializer, an argument, a return, an "=="/"!="
203
+ # comparison and the arms of "?:".
204
+ def self.null_pointer_constant?(node)
205
+ return true if integer_constant_zero?(node)
206
+
207
+ node.is_a?(Cast) && node.type.pointer? && node.type.target.void? &&
208
+ integer_constant_zero?(node.operand)
209
+ end
210
+
211
+ # Whether `node` is an integer constant expression (6.6) whose value is
212
+ # 0, via the shared ConstantEvaluator — not just a syntactic IntLit, so
213
+ # a foldable expression (an enum constant, "1 - 1", a cast to another
214
+ # integer type) is recognized too. Not a constant expression at all (a
215
+ # variable, a call, ...) or a division by a folded-zero divisor rescues
216
+ # to false rather than propagating, since this is a yes/no predicate,
217
+ # not a context that should surface either as a diagnostic.
218
+ def self.integer_constant_zero?(node)
219
+ ConstantEvaluator.evaluate(node).zero?
220
+ rescue ConstantEvaluator::NotConstant, ConstantEvaluator::DivisionByZero
221
+ false
222
+ end
223
+
224
+ # Simple assignment `target = value`. `target` is a VariableRef or a
225
+ # dereference (Unary with op :deref); the parser rejects any other,
226
+ # non-assignable target.
227
+ Assignment = Data.define(:target, :value, :token)
228
+
229
+ # An expression evaluated for its side effects; the value is discarded.
230
+ ExpressionStmt = Data.define(:expr, :token)
231
+
232
+ # The empty statement ";".
233
+ EmptyStmt = Data.define(:token)
234
+
235
+ # A GNU inline-assembly statement in its only accepted (degenerate) form
236
+ # (DESIGN R7): `__asm__` with optional volatile qualifiers and an empty
237
+ # template, e.g. `__asm__ volatile("" ::: "memory");`. The parser has
238
+ # already verified the template is empty and no real operand appears, so
239
+ # nothing is carried but the keyword token; it lowers to no code at all —
240
+ # rubycc never reorders, so a memory-clobber barrier is a natural no-op.
241
+ InlineAsm = Data.define(:token)
242
+
243
+ # `if (condition) then_stmt` with an optional `else else_stmt`.
244
+ # `else_stmt` is nil when there is no else clause.
245
+ If = Data.define(:condition, :then_stmt, :else_stmt, :token)
246
+
247
+ # A compound-statement "{ block-item* }" introducing a new scope.
248
+ # `items` is a flat array of declaration/statement nodes.
249
+ Block = Data.define(:items, :token)
250
+
251
+ # A GNU statement expression "( { block-item* } )" (a GCC extension, not
252
+ # ISO C). `body` is the AST::Block the parentheses wrap around a compound
253
+ # statement; the braces introduce the block's own scope, just like any
254
+ # compound-statement. When the block's last block-item is an
255
+ # expression-statement, that expression's value and type are the whole
256
+ # construct's; otherwise (an empty block, or a last item that is a
257
+ # declaration, a loop, a bare ";" and so on) the construct is void.
258
+ StatementExpr = Data.define(:body, :token)
259
+
260
+ # `while (condition) body`
261
+ While = Data.define(:condition, :body, :token)
262
+
263
+ # `do body while (condition);`
264
+ DoWhile = Data.define(:body, :condition, :token)
265
+
266
+ # `for (init; condition; step) body`. `init` is an array of
267
+ # VariableDecl (from a declaration clause), an expression node (from an
268
+ # expression clause) or nil (clause omitted). `condition` and `step`
269
+ # are expression nodes or nil when omitted.
270
+ For = Data.define(:init, :condition, :step, :body, :token)
271
+
272
+ # `break;` — targets the innermost enclosing iteration-statement or,
273
+ # once switch exists, the innermost enclosing switch.
274
+ Break = Data.define(:token)
275
+
276
+ # `continue;` — targets the innermost enclosing iteration-statement,
277
+ # passing straight through any switch that sits between it and the loop.
278
+ Continue = Data.define(:token)
279
+
280
+ # `switch (control) body` (ISO C 6.8.4.2). `control` is the controlling
281
+ # expression (an integer type). `body` is a single statement — usually a
282
+ # compound-statement — inside which `case`/`default` labels mark the entry
283
+ # points the generator lowers to a comparison chain. A case label may sit
284
+ # at any depth of a nested statement in `body`; the ones that belong to
285
+ # this switch are exactly those not enclosed by a nested switch.
286
+ Switch = Data.define(:control, :body, :token)
287
+
288
+ # A `case <constant>: statement` label (6.8.1). `value` is the case
289
+ # constant folded to a Ruby Integer at parse time (an integer or character
290
+ # constant, optionally signed — general constant-expression evaluation
291
+ # arrives in a later step). `body` is the labeled statement. A Case only
292
+ # has meaning inside a switch; the generator diagnoses one that is not.
293
+ Case = Data.define(:value, :body, :token)
294
+
295
+ # A `default: statement` label (6.8.1). `body` is the labeled statement.
296
+ # Like Case it is only meaningful inside a switch, and at most one may
297
+ # appear per switch.
298
+ Default = Data.define(:body, :token)
299
+
300
+ # A `goto identifier;` jump (6.8.6.1). `label` is the target label's name.
301
+ # The target may be defined anywhere in the same function, before or after
302
+ # the goto; the generator resolves the name against a function-scoped label
303
+ # table and diagnoses a jump to an undefined label at the function's end.
304
+ Goto = Data.define(:label, :token)
305
+
306
+ # A labeled statement `identifier: statement` (6.8.1), the target of a
307
+ # goto. `name` is the label's name and `body` the statement it prefixes
308
+ # (often the empty statement, as in "end: ;"). Distinguished from an
309
+ # ordinary expression-statement by two tokens of lookahead — an identifier
310
+ # immediately followed by ":".
311
+ Label = Data.define(:name, :body, :token)
312
+
313
+ # A function call "callee ( args )" (ISO C 6.5.2.2). `callee` is the
314
+ # postfix-expression being called — an ordinary identifier for a direct
315
+ # call ("f(x)"), but equally a dereference ("(*fp)(x)"), a subscript
316
+ # ("table[i](x)"), a member access ("s.fp(x)") or any expression that
317
+ # yields a function or a function pointer. `args` is an array of
318
+ # expression nodes (the argument-expression-list), evaluated left to
319
+ # right. The generator settles whether the callee is a direct function
320
+ # designator (a plain :call) or a pointer value (an indirect call).
321
+ Call = Data.define(:callee, :args, :token)
322
+
323
+ # "__builtin_va_start ( ap , last )" (the initializer of a variable
324
+ # argument list). `ap` is the va_list expression to initialize; `last_name`
325
+ # is the identifier String naming the function's last fixed parameter,
326
+ # which ISO C requires so the variable part can be located just past it.
327
+ # `token` is the builtin keyword, locating every diagnostic (a fixed-arity
328
+ # enclosing function, a wrong last-parameter name, a bad `ap` type). The
329
+ # expression's value is void — it is only ever an expression-statement.
330
+ VaStart = Data.define(:ap, :last_name, :token)
331
+
332
+ # "__builtin_va_arg ( ap , type-name )": fetches the next variable argument
333
+ # and advances `ap`. `ap` is the va_list expression and `type` is the
334
+ # resolved Rubycc::Type the argument is read as (an int/long/unsigned/
335
+ # pointer object type; a promotable or aggregate type is diagnosed). The
336
+ # whole expression's value and type are that argument.
337
+ VaArg = Data.define(:ap, :type, :token)
338
+
339
+ # "__builtin_va_end ( ap )": ends traversal of `ap`. On System V this is a
340
+ # no-op beyond type-checking the operand, so the node carries only the
341
+ # va_list expression and the keyword token; its value is void.
342
+ VaEnd = Data.define(:ap, :token)
343
+
344
+ # "__builtin_va_copy ( dest , src )": copies the traversal state of `src`
345
+ # into `dest` so the two can be walked independently (7.16.1.2). Both are
346
+ # va_list expressions; the generator lowers the copy to a whole-tag move
347
+ # between their (decayed) addresses. The value is void, like va_end.
348
+ VaCopy = Data.define(:dest, :src, :token)
349
+
350
+ # "__builtin_expect ( exp , c )": gcc's branch-prediction hint, typed
351
+ # `long(long, long)`. `exp` is the tested expression and `c` the value it
352
+ # is expected to take. rubycc has no optimizer, so the hint carries no
353
+ # weight: both operands are evaluated (left to right, `c` for its side
354
+ # effects) and the whole expression is `exp` converted to `long`. `token`
355
+ # is the builtin keyword.
356
+ BuiltinExpect = Data.define(:exp, :c, :token)
357
+
358
+ # "__builtin_alloca ( n )": reserves `n` bytes of automatic storage on the
359
+ # stack, released when the enclosing *function* returns. `size` is the
360
+ # byte-count expression (converted to size_t) and `token` the keyword; the
361
+ # whole expression's value is the block's base address, a `void *` the ABI
362
+ # keeps 16-byte aligned.
363
+ BuiltinAlloca = Data.define(:size, :token)
364
+
365
+ # "__builtin_offsetof ( type-name , member-designator )": the byte offset of
366
+ # a member within a struct/union, folded to a size_t constant. Unlike the
367
+ # traditional "((size_t)&(((t*)0)->m))" macro, it is a genuine
368
+ # constant-expression, so it holds in a static initializer, an array bound
369
+ # or a case label — the reason gcc/clang provide it. `type` is the resolved
370
+ # aggregate type; `designator` is the parsed member-designator, a non-empty
371
+ # array whose first element is always an OffsetofMember (the leading member
372
+ # name carries no "."). Each element is either an OffsetofMember (".name",
373
+ # or the leading bare name) or an OffsetofIndex ("[ constant-expression ]"),
374
+ # so a nested field ("a.b[2].c") reaches its target one element at a time.
375
+ # `token` is the builtin keyword, locating every diagnostic.
376
+ BuiltinOffsetof = Data.define(:type, :designator, :token)
377
+
378
+ # One member step of a __builtin_offsetof designator: the member named
379
+ # `name` of the aggregate reached so far. `token` is the member identifier,
380
+ # for a "no member named ..." diagnostic.
381
+ OffsetofMember = Data.define(:name, :token)
382
+
383
+ # One subscript step of a __builtin_offsetof designator: element `index`
384
+ # (a constant-expression node) of the array reached so far. `token` is the
385
+ # "[" token, for a "subscript of non-array" diagnostic.
386
+ OffsetofIndex = Data.define(:index, :token)
387
+
388
+ # "__builtin_constant_p ( expr )": gcc's compile-time-constant test, typed
389
+ # int. `expr` is the (unevaluated) operand and `token` the builtin keyword.
390
+ # The whole expression folds to 1 when `expr` reduces to a compile-time
391
+ # constant and 0 otherwise — including when it references a variable or a
392
+ # function call, which is not an error here (unlike an ordinary constant
393
+ # expression) but simply yields 0. `expr` is never evaluated, so it
394
+ # produces no code and no side effects.
395
+ BuiltinConstantP = Data.define(:expr, :token)
396
+
397
+ # "__builtin_ctz/ctzll/clz/clzll ( x )": counts the trailing (ctz) or
398
+ # leading (clz) zero bits of an integer, typed int. `operand` is the value
399
+ # scanned, `direction` is :forward for ctz or :reverse for clz, and `width`
400
+ # is the operand's byte width (4 for the plain form, 8 for the "ll" form).
401
+ # `x == 0` is undefined behavior (gcc), so no zero handling is implied.
402
+ # `token` is the builtin keyword.
403
+ BuiltinBitScan = Data.define(:operand, :direction, :width, :token)
404
+
405
+ # "__builtin_add/sub/mul_overflow ( a , b , res )": computes "a op b" with
406
+ # infinite precision, stores the result converted to the type `res` points
407
+ # at (wrapping or truncating like any integer conversion), and yields int 1
408
+ # when that conversion lost the value and 0 when it did not. `op` is :add,
409
+ # :sub or :mul and `args` holds the three argument expressions as written.
410
+ # The parser checks only the argument *count*; the operand types (both
411
+ # integers, `res` a pointer to an integer) need resolved types and are the
412
+ # generator's to diagnose. `token` is the builtin keyword.
413
+ #
414
+ # The two operands keep their own types — no usual arithmetic conversion
415
+ # runs between them, which is the whole point of the builtin: "int -1" plus
416
+ # "unsigned 1" is 0 in infinite precision, not UINT_MAX.
417
+ BuiltinOverflow = Data.define(:op, :args, :token)
418
+
419
+ # One of gcc's __atomic_* builtins. `kind` names the operation — :load,
420
+ # :store, :exchange, :compare_exchange, :fetch_add, :fetch_sub,
421
+ # :add_fetch, :sub_fetch, :or_fetch or :fence — and `args`
422
+ # holds the argument expressions exactly as written, including the trailing
423
+ # memory-order argument(s) and __atomic_compare_exchange_n's `weak` flag.
424
+ # The parser checks only the argument *count* here; the operand types (and
425
+ # the 4-or-8-byte width restriction) are the generator's to diagnose, since
426
+ # they need the resolved types. `token` is the builtin keyword.
427
+ #
428
+ # The memory-order arguments are carried through but never inspected: every
429
+ # operation is lowered at the strongest order (see
430
+ # IR::Generator#gen_builtin_atomic), which is why they need no constant
431
+ # folding here.
432
+ BuiltinAtomic = Data.define(:kind, :args, :token)
433
+
434
+ # One of gcc's legacy __sync_* builtins. `kind` names the operation —
435
+ # :fetch_add, :fetch_sub, :add_fetch, :sub_fetch, :or_fetch, :exchange,
436
+ # :release, :fence, :bool_compare_and_swap or :val_compare_and_swap — and
437
+ # `args` holds the argument expressions exactly as written. As with
438
+ # BuiltinAtomic the parser checks only the argument *count*; the operand
439
+ # types and the width restriction need resolved types and belong to the
440
+ # generator. `token` is the builtin keyword.
441
+ #
442
+ # This is a node of its own rather than a flag on BuiltinAtomic because the
443
+ # two families' argument *layouts* differ: the __sync_* forms are full
444
+ # barriers by definition and so carry no memory-order argument at all,
445
+ # while the compare-and-swap pair takes its expected value directly instead
446
+ # of through a pointer. Sharing a node would invite lowering code written
447
+ # for one layout to read the other's arguments by index.
448
+ BuiltinSync = Data.define(:kind, :args, :token)
449
+
450
+ # "__builtin_unreachable ()": marks a point control never reaches, typed
451
+ # void. rubycc performs no optimization, so it lowers to no code at all —
452
+ # its only role is to let constructs like CRuby's UNREACHABLE_RETURN
453
+ # ("(__builtin_unreachable(), value)") compile. `token` is the keyword.
454
+ BuiltinUnreachable = Data.define(:token)
455
+
456
+ # A single function parameter. `name` is the identifier String, or nil
457
+ # for an unnamed parameter in a prototype (e.g. "int f(int, int);").
458
+ # `type` is the parameter's Rubycc::Type (int, char or a pointer; never
459
+ # void, except as a pointer's target). `const` is true when the parameter
460
+ # object is top-level const-qualified ("int f(const int x)"), computed
461
+ # after the array/function-to-pointer adjustment, so the generator can
462
+ # reject writes to it.
463
+ #
464
+ # `incoming_type` is the type the *caller* hands over when that differs
465
+ # from the declared one, and nil (the common case) when the two coincide.
466
+ # Only an old-style (identifier-list) definition can separate them: such a
467
+ # function has no prototype, so its arguments arrive default-argument
468
+ # promoted (6.9.1p10 with 6.5.2.2p6) — a `float` parameter is passed a
469
+ # `double`, a `char`/`short`/`_Bool` parameter an `int` — and the body
470
+ # still sees the narrow object it declared. #abi_type is what the calling
471
+ # convention and the function's own type are built from; `type` is what
472
+ # the body reads and writes.
473
+ Parameter = Data.define(:name, :type, :token, :const, :incoming_type) do
474
+ def initialize(name:, type:, token:, const:, incoming_type: nil)
475
+ super
476
+ end
477
+
478
+ # The type this parameter occupies an ABI slot as.
479
+ def abi_type
480
+ incoming_type || type
481
+ end
482
+ end
483
+
484
+ # A function prototype (a bare declaration with no body), e.g.
485
+ # "int f(int a, int b);". `return_type` is the declared Rubycc::Type
486
+ # (int, char, void or a pointer). `params` is an array of Parameter.
487
+ # `storage` records the storage-class specifier (nil/:static/:extern) for
488
+ # Phase B; `inline` is accepted and folded away by the parser and left off
489
+ # here since the generator has no use for it yet. `variadic` is true when
490
+ # the prototype ends in "..." ("int printf(const char *, ...);"), in which
491
+ # case `params` holds only the fixed, named parameters.
492
+ FunctionDecl = Data.define(:name, :return_type, :params, :token, :storage, :variadic)
493
+
494
+ # A function definition. `return_type` is the declared Rubycc::Type
495
+ # (int, char, void or a pointer). `params` is an array of Parameter
496
+ # (each with a non-nil name) and `body` is an array of statement nodes.
497
+ # `storage` records the storage-class specifier (nil/:static/:extern) for
498
+ # Phase B, the generator not yet acting on it. `variadic` is true when the
499
+ # parameter list ends in "..." (the variable part is reachable only through
500
+ # va_* in Phase B; a body that ignores it compiles and runs already).
501
+ FunctionDef = Data.define(:name, :return_type, :params, :body, :token, :storage, :variadic)
502
+
503
+ # What a function's `__attribute__((constructor))` /
504
+ # `__attribute__((destructor))` asked for. Each field is nil when that
505
+ # attribute never appeared, or the priority it carried — the run-order
506
+ # number, with the parser's DEFAULT_INIT_PRIORITY standing for the
507
+ # unnumbered spelling. A function may be both, as
508
+ # "__attribute__((constructor,destructor))" is.
509
+ InitAttributes = Data.define(:constructor, :destructor)
510
+
511
+ # Whole translation unit. `functions` is an array of FunctionDef,
512
+ # FunctionDecl (prototype) and GlobalDecl (file-scope variable) nodes in
513
+ # source order. `init_attributes` maps a function name to the
514
+ # InitAttributes its declarations carried; it is a property of the unit
515
+ # rather than of any one node because the attribute may be written on a
516
+ # prototype that precedes or follows the definition (see
517
+ # Parser#register_init_attributes). Names never defined here appear in it
518
+ # too and are dropped by the generator. `visibility_attributes` maps an
519
+ # identifier to an explicit GNU ELF visibility; it is kept at program
520
+ # scope because an exported declaration often precedes its definition.
521
+ Program = Data.define(:functions, :init_attributes, :visibility_attributes) do
522
+ def initialize(functions:, init_attributes: {}, visibility_attributes: {})
523
+ super
524
+ end
525
+ end
526
+ end
527
+ end
528
+ end