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,592 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ast"
4
+ require_relative "../type"
5
+ require_relative "../compile_error"
6
+
7
+ module Rubycc
8
+ module Front
9
+ # One scalar placed by an initializer: `value` (an expression node) writes
10
+ # `type` (a scalar Rubycc::Type) at byte `offset` from the object's start.
11
+ # The caller evaluates `value` in its own context — folded to a constant for
12
+ # a global, generated and narrowed for a local — so the resolver stays free
13
+ # of the generator and of constant evaluation.
14
+ ScalarInit = Data.define(:offset, :type, :value)
15
+
16
+ # A scalar placed into a bit-field. `offset` identifies the containing
17
+ # storage unit; `shift` is the field's bit position within that unit. It
18
+ # stays separate from ScalarInit because a bit-field initializer must be a
19
+ # read-modify-write, not a whole-width scalar store at the member's byte.
20
+ BitfieldInit = Data.define(:offset, :type, :value, :width, :shift)
21
+
22
+ # A run of literal bytes a string initializer places at `offset` (the raw
23
+ # string-literal bytes, without the terminating NUL). The caller zero-fills
24
+ # the whole object first, so the NUL and any trailing array slots come for
25
+ # free and are not recorded here.
26
+ StringInit = Data.define(:offset, :bytes)
27
+
28
+ # A whole aggregate expression copied into an aggregate subobject at
29
+ # `offset`. C permits an initializer-list element to be a single expression
30
+ # of the subobject's own structure/union type (6.7.9p13) -- a compound
31
+ # literal such as `.base = (Node){ .type = 1 }`, a call returning that
32
+ # struct, a variable of it; it is an expression initializer, not a
33
+ # brace-elided walk of the outer cursor.
34
+ AggregateInit = Data.define(:offset, :type, :value)
35
+
36
+ # The outcome of resolving an initializer: `type` is the object's type with
37
+ # any "[]" array bound now filled in (so sizeof and the storage layout see a
38
+ # complete type), `entries` is the flat list of ScalarInit/BitfieldInit/
39
+ # StringInit/AggregateInit placements
40
+ # placements, in source order, that the caller lowers, and `flexible_bytes`
41
+ # is the storage an initialized flexible array member adds *beyond*
42
+ # `type.size` (0 for every other object; see #fill_flexible_array_member).
43
+ ResolvedInitializer = Data.define(:type, :entries, :flexible_bytes)
44
+
45
+ # Resolves an initializer (6.7.9) against the object's type into a flat list
46
+ # of scalar/string placements plus a completed type. The whole current-object
47
+ # walk lives here — nested braces, brace elision (an aggregate subobject with
48
+ # no braces of its own keeps drawing from the enclosing list), designated
49
+ # initializers ("[i] = ", ".m = ", and chains such as ".a.b = "), the "{0}"
50
+ # idiom, unions (the first member by default, any member by designator) and
51
+ # transparent designation into an anonymous member — so both the local and
52
+ # the global lowering share one interpretation and neither re-implements it.
53
+ #
54
+ # What it deliberately leaves out: it never evaluates a scalar's value (that
55
+ # is the caller's, since a constant fold and a run-time store differ), and it
56
+ # never type-checks a scalar against its slot (the caller applies the
57
+ # ordinary assignment conversion). It only diagnoses the structural errors —
58
+ # excess elements, a braced list for a scalar, an unknown member, an
59
+ # out-of-range index and an over-long char-array string — that
60
+ # depend on the shape alone.
61
+ class InitializerResolver
62
+ # A forward cursor over one brace level's items. Brace elision hands the
63
+ # same cursor to a subobject's fill so it continues where the parent left
64
+ # off; an explicit nested brace gets a fresh cursor over its own items.
65
+ class Cursor
66
+ def initialize(items)
67
+ @items = items
68
+ @pos = 0
69
+ end
70
+
71
+ def eof?
72
+ @pos >= @items.size
73
+ end
74
+
75
+ def peek
76
+ @items[@pos]
77
+ end
78
+
79
+ def advance
80
+ item = @items[@pos]
81
+ @pos += 1
82
+ item
83
+ end
84
+ end
85
+
86
+ # Whether `init` initializes `type` structurally (needs this resolver)
87
+ # rather than as a plain scalar value: a brace list always does, and so
88
+ # does a bare string literal aimed at a char array. Both the parser (to
89
+ # decide whether to fold or defer) and the generator consult this.
90
+ def self.structural?(type, init)
91
+ init.is_a?(AST::InitializerList) ||
92
+ (char_array?(type) && init.is_a?(AST::StringLit))
93
+ end
94
+
95
+ # An array of character type — the one aggregate a string literal may
96
+ # initialize as a whole (6.7.9p14). Any of the three character types
97
+ # qualifies (plain `char` under either target signedness, `signed char`,
98
+ # `unsigned char`), since the literal's bytes are copied in unchanged.
99
+ def self.char_array?(type)
100
+ type.array? && Type.character?(type.element)
101
+ end
102
+
103
+ # `static_storage` says the object being initialized lives in .data/.bss
104
+ # (a file-scope definition or a block-scope `static`), which is the only
105
+ # place a trailing flexible array member may be initialized.
106
+ #
107
+ # `type_of` is how the caller lends this resolver its view of expression
108
+ # types: a callable taking an expression node and answering its
109
+ # Rubycc::Type, or nil when it cannot tell (see #single_expression_init?,
110
+ # which is the only thing that asks). It must not emit code or evaluate
111
+ # anything — the generator passes its #static_type inference, the parser
112
+ # the smaller one it uses for parse-time sizeof. A caller with no type
113
+ # table at all may leave it out, and every subobject then falls to brace
114
+ # elision as it did before the hook existed.
115
+ def self.resolve(type, initializer, static_storage: false, type_of: nil)
116
+ new.resolve(type, initializer, static_storage: static_storage, type_of: type_of)
117
+ end
118
+
119
+ def resolve(type, initializer, static_storage: false, type_of: nil)
120
+ @entries = []
121
+ @static_storage = static_storage
122
+ @type_of = type_of
123
+ @flexible_bytes = 0
124
+ # The first expression this run could not type against a struct
125
+ # subobject, if any; #excess_elements_error reports against it.
126
+ @untyped_struct_item = nil
127
+ # Only the object's *own* trailing flexible array member may be
128
+ # initialized; one reached through a nested struct is rejected, so
129
+ # remember which struct owns the one initializer this run may fill.
130
+ @flexible_owner = type.struct? ? type : nil
131
+ final = init_top(type, initializer)
132
+ ResolvedInitializer.new(final, @entries, @flexible_bytes)
133
+ end
134
+
135
+ private
136
+
137
+ # The outermost object. A char array taking a (possibly braced) string,
138
+ # then a scalar taking a plain expression, are the two non-list forms;
139
+ # everything else is a brace list dispatched by the object's kind.
140
+ def init_top(type, initializer)
141
+ if self.class.char_array?(type) && (bytes = string_bytes(initializer))
142
+ return place_string(type, 0, bytes, initializer)
143
+ end
144
+
145
+ unless initializer.is_a?(AST::InitializerList)
146
+ return place_scalar_value(type, 0, initializer)
147
+ end
148
+
149
+ init_object_from_list(type, 0, initializer)
150
+ end
151
+
152
+ # A scalar initialized by a plain (non-brace) expression: record it as is.
153
+ # Only a scalar reaches here off the non-list path; an aggregate without
154
+ # braces (and without the char-array string form) has no meaning.
155
+ def place_scalar_value(type, offset, value)
156
+ unless scalar?(type)
157
+ error(value.token, "invalid initializer for aggregate (expected '{')")
158
+ end
159
+ @entries << ScalarInit.new(offset, type, value)
160
+ type
161
+ end
162
+
163
+ # Initializes `type` at `base` from an explicit brace list. A scalar in
164
+ # braces ("int x = {5};") is unwrapped; an aggregate is walked by its kind,
165
+ # then any leftover item is an excess element.
166
+ def init_object_from_list(type, base, list)
167
+ # GCC accepts the empty compound literal used by yajl-ruby, and C23
168
+ # standardizes the same zero-initializer form. The lowering zero-fills
169
+ # the whole object before applying entries, so no entry is needed here.
170
+ return type if list.items.empty?
171
+
172
+ return init_scalar_from_list(type, base, list) if scalar?(type)
173
+
174
+ require_layout(type, list.token)
175
+ cursor = Cursor.new(list.items)
176
+ final = fill_aggregate(type, base, cursor)
177
+ unless cursor.eof?
178
+ excess_elements_error(item_token(cursor.peek), "excess elements in initializer")
179
+ end
180
+ final
181
+ end
182
+
183
+ # "int x = {5};" (6.7.9p11): exactly one item, no designator, no inner
184
+ # brace; more than one item, or a designator, or a nested brace is an
185
+ # error.
186
+ def init_scalar_from_list(type, base, list)
187
+ first = list.items.first
188
+ unless first.designators.empty?
189
+ error(first.designators.first.token, "designator in initializer for scalar type")
190
+ end
191
+ if first.value.is_a?(AST::InitializerList)
192
+ error(first.value.token, "too many braces around scalar initializer")
193
+ end
194
+ if list.items.size > 1
195
+ excess_elements_error(item_token(list.items[1]), "excess elements in scalar initializer")
196
+ end
197
+ @entries << ScalarInit.new(base, type, first.value)
198
+ type
199
+ end
200
+
201
+ # Raises the excess-element diagnostic `message`, unless this run already
202
+ # met an expression it could not type against a struct subobject -- in
203
+ # which case that expression, not the leftover item, is what the report has
204
+ # to name.
205
+ #
206
+ # The two are the same event seen from different ends. Without a `type_of`
207
+ # hook (the parser completing a declaration's type, docs/development/GAPS.md gap T) an
208
+ # expression standing at a struct subobject cannot be told from the start
209
+ # of an elided brace group, so the walk assumes elision, descends into the
210
+ # struct's members, consumes the following items to fill them, and runs off
211
+ # the end of the list. The overrun is a *consequence*: reporting it names
212
+ # an item that is entirely well-formed ("{5,6}" in
213
+ # `pt b[] = { {1,2}, fp(), {5,6} };`) and says "scalar", which sends the
214
+ # reader looking for a scalar that is not there. The generator re-resolves
215
+ # the same initializer with the hook and gets this right, so the message
216
+ # below is only ever what the type-completion pass produces.
217
+ def excess_elements_error(token, message)
218
+ return error(token, message) unless @untyped_struct_item
219
+
220
+ error(@untyped_struct_item.token,
221
+ "unsupported initializer: rubycc cannot tell whether this expression initializes " \
222
+ "a whole struct element, because its type is not known while the declaration is parsed")
223
+ end
224
+
225
+ # Dispatches an aggregate to its kind-specific fill, returning the (possibly
226
+ # length-completed) type. Only a top-level "[]" array is ever incomplete;
227
+ # a nested one always has a fixed bound.
228
+ def fill_aggregate(type, base, cursor)
229
+ if type.array?
230
+ max = fill_array(type, base, cursor)
231
+ type.length ? type : Type::Array.new(type.element, max + 1)
232
+ elsif type.union?
233
+ fill_union(type, base, cursor)
234
+ type
235
+ else
236
+ fill_struct(type, base, cursor)
237
+ type
238
+ end
239
+ end
240
+
241
+ # Fills array elements in order, honoring "[i]" designators (which jump the
242
+ # cursor and continue from i+1) and stopping when the fixed bound is reached
243
+ # or a designator that is not ours appears (it belongs to an enclosing
244
+ # object). Returns the greatest index touched, so a top-level "[]" bound can
245
+ # be inferred as that plus one.
246
+ def fill_array(type, base, cursor)
247
+ element = type.element
248
+ index = 0
249
+ highest = -1
250
+ until cursor.eof?
251
+ item = cursor.peek
252
+ designator = item.designators.first
253
+ if designator
254
+ break unless designator.is_a?(AST::ArrayDesignator)
255
+
256
+ index = designator.index
257
+ check_array_index(type, designator)
258
+ rest = item.designators[1..]
259
+ else
260
+ break if type.length && index >= type.length
261
+
262
+ rest = []
263
+ end
264
+ init_subobject(element, base + index * element.size, cursor, item, rest)
265
+ highest = index if index > highest
266
+ index += 1
267
+ end
268
+ highest
269
+ end
270
+
271
+ # Fills struct members in order, honoring ".m" designators (which reposition
272
+ # to that member — transparently into an anonymous member — and continue
273
+ # from the one after) and stopping at the last member or a non-member
274
+ # designator meant for an enclosing object.
275
+ def fill_struct(type, base, cursor)
276
+ members = type.members
277
+ index = 0
278
+ until cursor.eof?
279
+ item = cursor.peek
280
+ designator = item.designators.first
281
+ if designator
282
+ break unless designator.is_a?(AST::MemberDesignator)
283
+
284
+ member, index = locate_member(type, designator)
285
+ fill_member(type, member, base, cursor, item, item.designators[1..], designated: true)
286
+ index += 1
287
+ else
288
+ break if index >= members.size
289
+
290
+ member = members[index]
291
+ fill_member(type, member, base, cursor, item, [])
292
+ index += 1
293
+ end
294
+ end
295
+ end
296
+
297
+ # Places `item` into one member of `type`. A flexible array member takes
298
+ # the separate path below, since it has no reserved storage of its own and
299
+ # so decides how much the object grows.
300
+ def fill_member(type, member, base, cursor, item, designators, designated: false)
301
+ if flexible_array_member?(member)
302
+ fill_flexible_array_member(type, member, base, cursor, item, designators)
303
+ else
304
+ init_subobject(member.type, base + member.offset, cursor, item, designators, member,
305
+ designated: designated)
306
+ end
307
+ end
308
+
309
+ # The trailing "T name[]" of a struct (6.7.2.1p18): an array member with
310
+ # no bound, hence an incomplete type.
311
+ def flexible_array_member?(member)
312
+ member.type.array? && member.type.incomplete?
313
+ end
314
+
315
+ # An initializer directed at a flexible array member. ISO C reserves no
316
+ # storage for one (it contributes nothing to sizeof), so the standard has
317
+ # no meaning for this; gcc extends the language to allow it for an object
318
+ # with static storage duration, laying the elements out at the member's
319
+ # offset and widening the *object* past its type's size. Measured against
320
+ # gcc: the object occupies sizeof(struct) + n * sizeof(element) bytes for
321
+ # n elements (so the struct's own trailing padding stays, and the extra
322
+ # bytes ride behind it), the count n is the highest element index any item
323
+ # reaches plus one whatever order the items arrive in, and an initialized
324
+ # FAM at automatic storage duration, or one reached through a nested
325
+ # struct, is rejected. `@flexible_bytes` carries n * sizeof(element) out
326
+ # to the caller, which sizes the object's image by it.
327
+ def fill_flexible_array_member(type, member, base, cursor, item, designators)
328
+ unless type.equal?(@flexible_owner)
329
+ error(item_token(item), "initialization of a flexible array member in a nested context")
330
+ end
331
+ unless @static_storage
332
+ error(item_token(item), "non-static initialization of a flexible array member")
333
+ end
334
+
335
+ element = member.type.element
336
+ offset = base + member.offset
337
+ count = flexible_array_count(member, offset, cursor, item, designators)
338
+ @flexible_bytes = count * element.size if count * element.size > @flexible_bytes
339
+ end
340
+
341
+ # How many elements an item directed at a flexible array member reaches.
342
+ # A leading "[i]" designator names one element, so it reaches i + 1; every
343
+ # other form (a brace list, a string, or brace elision drawing from the
344
+ # enclosing list) fills the member as a whole, and the bound the fill
345
+ # infers for the unbounded array is the count.
346
+ def flexible_array_count(member, offset, cursor, item, designators)
347
+ if designators.first.is_a?(AST::ArrayDesignator)
348
+ index = designators.first.index
349
+ init_subobject(member.type, offset, cursor, item, designators, member)
350
+ index + 1
351
+ else
352
+ completed = init_subobject(member.type, offset, cursor, item, designators, member)
353
+ (completed.is_a?(Type::Array) && completed.length) || 0
354
+ end
355
+ end
356
+
357
+ # A union holds one member at a time: the first by default, or the one a
358
+ # leading ".m" designator selects. Exactly one item is consumed here; any
359
+ # further items are surfaced as excess by the enclosing list.
360
+ def fill_union(type, base, cursor)
361
+ return if cursor.eof?
362
+
363
+ item = cursor.peek
364
+ designator = item.designators.first
365
+ if designator.is_a?(AST::MemberDesignator)
366
+ member, = locate_member(type, designator)
367
+ init_subobject(member.type, base + member.offset, cursor, item, item.designators[1..], member,
368
+ designated: true)
369
+ elsif designator
370
+ # An array designator here is for an enclosing object; leave it.
371
+ nil
372
+ else
373
+ member = type.members.first
374
+ init_subobject(member.type, base + member.offset, cursor, item, [], member)
375
+ end
376
+ end
377
+
378
+ # Places one item into the subobject at (sub_type, sub_offset). A remaining
379
+ # designator chain first steps deeper (".a.b" / "[1].m"); with none left,
380
+ # the item's value initializes the subobject: a char array takes a string,
381
+ # a nested brace recurses with its own cursor, a scalar is recorded, and a
382
+ # braceless aggregate elides — it keeps drawing from the *same* cursor.
383
+ def init_subobject(sub_type, sub_offset, cursor, item, designators, member = nil, designated: false)
384
+ unless designators.empty?
385
+ inner_type, inner_offset, inner_member = step_designator(sub_type, sub_offset, designators.first)
386
+ return init_subobject(inner_type, inner_offset, cursor, item, designators[1..], inner_member,
387
+ designated: true)
388
+ end
389
+
390
+ value = item.value
391
+ if self.class.char_array?(sub_type) && (bytes = string_bytes(value))
392
+ cursor.advance
393
+ place_string(sub_type, sub_offset, bytes, value)
394
+ elsif value.is_a?(AST::InitializerList)
395
+ cursor.advance
396
+ init_object_from_list(sub_type, sub_offset, value)
397
+ elsif single_expression_init?(sub_type, value, designated)
398
+ cursor.advance
399
+ @entries << AggregateInit.new(sub_offset, sub_type, value)
400
+ elsif scalar?(sub_type)
401
+ cursor.advance
402
+ if member&.bitfield?
403
+ unit_bits = member.type.size * 8
404
+ unit_offset = (member.bit_offset / unit_bits) * member.type.size
405
+ shift = member.bit_offset % unit_bits
406
+ unit_base = sub_offset - member.offset + unit_offset
407
+ @entries << BitfieldInit.new(unit_base, sub_type, value, member.bit_width, shift)
408
+ else
409
+ @entries << ScalarInit.new(sub_offset, sub_type, value)
410
+ end
411
+ else
412
+ # Brace elision (6.7.9p20): a braceless aggregate subobject consumes as
413
+ # many following items as it needs from the shared cursor.
414
+ require_layout(sub_type, value.token)
415
+ fill_aggregate(sub_type, sub_offset, cursor)
416
+ end
417
+ end
418
+
419
+ # An incomplete struct/union has no known layout, so it cannot be
420
+ # initialized; the generator's own completeness guards never see it,
421
+ # because the resolver walks the type first.
422
+ def require_layout(type, token)
423
+ return unless type.struct? && !type.complete?
424
+
425
+ error(token, "initialization of incomplete type '#{type}'")
426
+ end
427
+
428
+ # Records a char array's string initializer and, for a top-level "[]",
429
+ # completes its bound to the string length plus the NUL. An array sized
430
+ # exactly to the character count (excluding the NUL) is allowed (6.7.9p14):
431
+ # the terminating NUL is simply dropped, so "char x[3] = \"abc\"" fills the
432
+ # array with no room to spare. Only a string longer than the array — one
433
+ # whose characters alone will not fit — is diagnosed as too long.
434
+ def place_string(type, offset, bytes, node)
435
+ length = type.length || bytes.bytesize + 1
436
+ if length < bytes.bytesize
437
+ error(node.token, "initializer-string for char array is too long")
438
+ end
439
+ @entries << StringInit.new(offset, bytes)
440
+ Type::Array.new(type.element, length)
441
+ end
442
+
443
+ # Steps one designator into an aggregate, returning the [type, offset] of
444
+ # the named/indexed subobject; a member step resolves transparently through
445
+ # anonymous members.
446
+ def step_designator(type, base, designator)
447
+ if designator.is_a?(AST::MemberDesignator)
448
+ unless type.struct?
449
+ error(designator.token, "field designator '.#{designator.name}' in non-struct initializer")
450
+ end
451
+ member = type.member(designator.name) ||
452
+ error(designator.token, "unknown field designator '.#{designator.name}'")
453
+ [member.type, base + member.offset, member]
454
+ else
455
+ unless type.array?
456
+ error(designator.token, "array designator in non-array initializer")
457
+ end
458
+ check_array_index(type, designator)
459
+ [type.element, base + designator.index * type.element.size, nil]
460
+ end
461
+ end
462
+
463
+ # The member a ".m" designator names, plus the index in `type.members` to
464
+ # continue positional filling from. A direct member gives its own index; a
465
+ # name reached through an anonymous member gives that anonymous member's
466
+ # index (so the next positional item lands after it).
467
+ def locate_member(type, designator)
468
+ direct = type.members.index { |m| m.name == designator.name }
469
+ return [type.members[direct], direct] if direct
470
+
471
+ member = type.member(designator.name) ||
472
+ error(designator.token, "unknown field designator '.#{designator.name}'")
473
+ containing = type.members.index do |m|
474
+ m.name.nil? && m.type.struct? && m.type.member(designator.name)
475
+ end
476
+ [member, containing || type.members.size - 1]
477
+ end
478
+
479
+ # Rejects a negative or past-the-end array designator; a top-level "[]"
480
+ # (unknown bound) only rejects a negative index.
481
+ def check_array_index(type, designator)
482
+ if designator.index.negative? || (type.length && designator.index >= type.length)
483
+ error(designator.token, "array designator index #{designator.index} exceeds array bounds")
484
+ end
485
+ end
486
+
487
+ # The raw bytes of a string that initializes a char array — a bare string
488
+ # literal, or a brace wrapping exactly one undesignated string literal
489
+ # ('char s[] = { "hi" };'); anything else is not a string initializer.
490
+ def string_bytes(node)
491
+ return node.value if node.is_a?(AST::StringLit)
492
+
493
+ if node.is_a?(AST::InitializerList) && node.items.size == 1
494
+ item = node.items.first
495
+ return item.value.value if item.designators.empty? && item.value.is_a?(AST::StringLit)
496
+ end
497
+ nil
498
+ end
499
+
500
+ def scalar?(type)
501
+ !type.array? && !type.struct?
502
+ end
503
+
504
+ # Whether `value` initializes the whole subobject as a single expression,
505
+ # rather than being the next item brace elision drops into the subobject's
506
+ # innermost member. 6.7.9p13 draws that line by *type*, not by syntax and
507
+ # not by whether a designator named the subobject: the initializer for a
508
+ # structure or union object is either a brace list "or a single expression
509
+ # that has compatible structure or union type". So the question here is
510
+ # only whether the expression's type is that of the struct/union subobject
511
+ # standing at the cursor.
512
+ #
513
+ # A designator does not enter into it, which is why the earlier
514
+ # approximation (any call/variable/member expression counts as an
515
+ # aggregate when designated, never when reached positionally) was wrong in
516
+ # both directions, as measured against gcc:
517
+ #
518
+ # MapInit m = { map, TypeInfo_get(f), TypeInfo_get(g), arena };
519
+ #
520
+ # has no designator anywhere, yet gcc initializes the two TypeInfo members
521
+ # from the calls that return TypeInfo (google-protobuf's message.c writes
522
+ # exactly this, and rubycc rejected it with "incompatible types in
523
+ # initialization"); while
524
+ #
525
+ # pt a[] = { 1, 2, 3, f(), 9, 10 }; /* int f(void) */
526
+ #
527
+ # is a two-element array for gcc because f() is an ordinary scalar item
528
+ # mid-elision -- and a three-element one when the same call returns pt,
529
+ # since then the expression fills a whole element. Only the type tells the
530
+ # two apart.
531
+ #
532
+ # An expression whose type is unknown (no `type_of` hook, or a form it
533
+ # does not cover) stays on the brace-elision path, which is what preserves
534
+ # `{ 0 }` and the ordinary positional rules.
535
+ # `designated` says a designator named this subobject, and only matters
536
+ # when no `type_of` hook was lent (the parser resolves with no type table
537
+ # at all, purely to complete an inferred array bound). There, a designator
538
+ # naming a struct/union subobject whose item is not a brace list leaves
539
+ # exactly one reading open under 6.7.9p13 -- the single-expression form --
540
+ # because brace elision cannot descend into a subobject the designator
541
+ # already selected. Guessing that way keeps the parser's type completion
542
+ # working; the generator re-resolves the same initializer *with* the hook,
543
+ # so a genuinely mistyped expression is still caught, by the pass that can
544
+ # actually see the type.
545
+ def single_expression_init?(sub_type, value, designated = false)
546
+ return false unless sub_type.struct?
547
+
548
+ value_type = expression_type(value)
549
+ if value_type.nil?
550
+ # Remember the first expression this run had to guess about, so a
551
+ # later overrun can name it instead of the item it stopped on (see
552
+ # #excess_elements_error). Only the guess that assumes brace elision
553
+ # can overrun; a designated one fills the subobject outright.
554
+ @untyped_struct_item ||= value unless designated
555
+ return designated
556
+ end
557
+
558
+ value_type.struct? && value_type == sub_type
559
+ end
560
+
561
+ # The type of an initializer item's expression, or nil when it cannot be
562
+ # told here. A compound literal and a cast name their own type, so they
563
+ # are answered without help and identically on every caller's path;
564
+ # everything else needs the surrounding scope and goes to the caller's
565
+ # `type_of` hook.
566
+ def expression_type(node)
567
+ case node
568
+ when AST::CompoundLiteral, AST::Cast
569
+ node.type
570
+ else
571
+ @type_of&.call(node)
572
+ end
573
+ end
574
+
575
+ # The token that locates an item for diagnostics: its first designator, or
576
+ # its value.
577
+ def item_token(item)
578
+ item.designators.first&.token || item.value.token
579
+ end
580
+
581
+ def error(token, description)
582
+ raise CompileError.new(
583
+ description,
584
+ filename: token.filename,
585
+ line: token.line,
586
+ column: token.column,
587
+ source_line: token.source_line
588
+ )
589
+ end
590
+ end
591
+ end
592
+ end