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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +56 -0
- data/LICENSE.txt +21 -0
- data/NOTICE +52 -0
- data/README.md +208 -0
- data/data/README.md +117 -0
- data/data/r10_corpus_scan.json +4082 -0
- data/data/r10_manual_classification.json +5342 -0
- data/data/r10_verification_m4a.json +1531 -0
- data/data/r10_verification_m4b.json +1818 -0
- data/data/r10_verification_m4c.json +1489 -0
- data/data/r10_verification_m4d.json +318 -0
- data/data/r10_verification_rbs.json +192 -0
- data/data/verified_gems.json +397 -0
- data/exe/rmake +16 -0
- data/exe/rubycc +9 -0
- data/exe/rubycc-ar +114 -0
- data/exe/rubycc-doctor +14 -0
- data/exe/rubycc-pkgconf +8 -0
- data/include/float.h +87 -0
- data/include/iso646.h +18 -0
- data/include/libc/alloca.h +18 -0
- data/include/libc/arpa/inet.h +61 -0
- data/include/libc/assert.h +43 -0
- data/include/libc/dirent.h +72 -0
- data/include/libc/dlfcn.h +58 -0
- data/include/libc/features.h +205 -0
- data/include/libc/glibc/aarch64/ctype.h +108 -0
- data/include/libc/glibc/aarch64/endian.h +54 -0
- data/include/libc/glibc/aarch64/errno.h +154 -0
- data/include/libc/glibc/aarch64/fcntl.h +144 -0
- data/include/libc/glibc/aarch64/inttypes.h +182 -0
- data/include/libc/glibc/aarch64/limits.h +63 -0
- data/include/libc/glibc/aarch64/pthread.h +131 -0
- data/include/libc/glibc/aarch64/setjmp.h +84 -0
- data/include/libc/glibc/aarch64/stdint.h +174 -0
- data/include/libc/glibc/aarch64/sys/epoll.h +88 -0
- data/include/libc/glibc/aarch64/sys/fcntl.h +19 -0
- data/include/libc/glibc/aarch64/sys/select.h +75 -0
- data/include/libc/glibc/aarch64/sys/stat.h +136 -0
- data/include/libc/glibc/aarch64/sys/syscall.h +167 -0
- data/include/libc/glibc/aarch64/sys/time.h +74 -0
- data/include/libc/glibc/aarch64/sys/types.h +127 -0
- data/include/libc/glibc/aarch64/time.h +113 -0
- data/include/libc/glibc/x86_64/ctype.h +108 -0
- data/include/libc/glibc/x86_64/endian.h +54 -0
- data/include/libc/glibc/x86_64/errno.h +154 -0
- data/include/libc/glibc/x86_64/fcntl.h +134 -0
- data/include/libc/glibc/x86_64/inttypes.h +182 -0
- data/include/libc/glibc/x86_64/limits.h +61 -0
- data/include/libc/glibc/x86_64/pthread.h +111 -0
- data/include/libc/glibc/x86_64/setjmp.h +82 -0
- data/include/libc/glibc/x86_64/stdint.h +163 -0
- data/include/libc/glibc/x86_64/sys/epoll.h +91 -0
- data/include/libc/glibc/x86_64/sys/fcntl.h +19 -0
- data/include/libc/glibc/x86_64/sys/select.h +75 -0
- data/include/libc/glibc/x86_64/sys/stat.h +130 -0
- data/include/libc/glibc/x86_64/sys/syscall.h +188 -0
- data/include/libc/glibc/x86_64/sys/time.h +74 -0
- data/include/libc/glibc/x86_64/sys/types.h +123 -0
- data/include/libc/glibc/x86_64/time.h +113 -0
- data/include/libc/grp.h +50 -0
- data/include/libc/langinfo.h +126 -0
- data/include/libc/link.h +18 -0
- data/include/libc/locale.h +83 -0
- data/include/libc/math.h +191 -0
- data/include/libc/netinet/in.h +126 -0
- data/include/libc/netinet/tcp.h +44 -0
- data/include/libc/poll.h +46 -0
- data/include/libc/pwd.h +58 -0
- data/include/libc/regex.h +53 -0
- data/include/libc/sched.h +35 -0
- data/include/libc/signal.h +204 -0
- data/include/libc/stdio.h +157 -0
- data/include/libc/stdlib.h +92 -0
- data/include/libc/string.h +74 -0
- data/include/libc/strings.h +25 -0
- data/include/libc/sys/cdefs.h +126 -0
- data/include/libc/sys/inotify.h +111 -0
- data/include/libc/sys/ioctl.h +36 -0
- data/include/libc/sys/mman.h +65 -0
- data/include/libc/sys/param.h +41 -0
- data/include/libc/sys/resource.h +109 -0
- data/include/libc/sys/socket.h +167 -0
- data/include/libc/sys/statfs.h +82 -0
- data/include/libc/sys/timerfd.h +55 -0
- data/include/libc/sys/uio.h +40 -0
- data/include/libc/sys/un.h +25 -0
- data/include/libc/sys/utsname.h +35 -0
- data/include/libc/sys/wait.h +135 -0
- data/include/libc/termios.h +179 -0
- data/include/libc/unistd.h +194 -0
- data/include/stdalign.h +16 -0
- data/include/stdarg.h +31 -0
- data/include/stdatomic.h +158 -0
- data/include/stdbool.h +15 -0
- data/include/stdckdint.h +28 -0
- data/include/stddef.h +60 -0
- data/include/stdnoreturn.h +18 -0
- data/include/x86intrin.h +16 -0
- data/lib/rubycc/backend/aarch64.rb +1724 -0
- data/lib/rubycc/backend/x86_64.rb +1369 -0
- data/lib/rubycc/compile_error.rb +36 -0
- data/lib/rubycc/compiler.rb +305 -0
- data/lib/rubycc/doctor/builder.rb +151 -0
- data/lib/rubycc/doctor/cli.rb +190 -0
- data/lib/rubycc/doctor/fetcher.rb +84 -0
- data/lib/rubycc/doctor/gemfile.rb +115 -0
- data/lib/rubycc/doctor/verified_gems.rb +114 -0
- data/lib/rubycc/doctor.rb +16 -0
- data/lib/rubycc/driver.rb +463 -0
- data/lib/rubycc/front/ast.rb +528 -0
- data/lib/rubycc/front/constant_evaluator.rb +631 -0
- data/lib/rubycc/front/initializer_resolver.rb +592 -0
- data/lib/rubycc/front/lexeme_reader.rb +460 -0
- data/lib/rubycc/front/lexer.rb +232 -0
- data/lib/rubycc/front/parser.rb +4122 -0
- data/lib/rubycc/front/token.rb +49 -0
- data/lib/rubycc/ir/call_convention.rb +486 -0
- data/lib/rubycc/ir/generator.rb +6036 -0
- data/lib/rubycc/ir/ir.rb +417 -0
- data/lib/rubycc/link/compat_runtime.rb +89 -0
- data/lib/rubycc/link/errors.rb +13 -0
- data/lib/rubycc/link/executable_linker.rb +405 -0
- data/lib/rubycc/link/library_resolver.rb +437 -0
- data/lib/rubycc/link/partial_linker.rb +546 -0
- data/lib/rubycc/link/shared_linker.rb +1732 -0
- data/lib/rubycc/mkmf_shim.rb +83 -0
- data/lib/rubycc/objfile/ar_archive.rb +437 -0
- data/lib/rubycc/objfile/elf_reader.rb +646 -0
- data/lib/rubycc/objfile/elf_writer.rb +891 -0
- data/lib/rubycc/objfile/relocatable_writer.rb +376 -0
- data/lib/rubycc/pkgconf/cli.rb +139 -0
- data/lib/rubycc/pkgconf/errors.rb +39 -0
- data/lib/rubycc/pkgconf/model.rb +28 -0
- data/lib/rubycc/pkgconf/parser.rb +109 -0
- data/lib/rubycc/pkgconf/pkgconf.rb +15 -0
- data/lib/rubycc/pkgconf/resolver.rb +70 -0
- data/lib/rubycc/pkgconf/search_path.rb +38 -0
- data/lib/rubycc/pkgconf/system_path_filter.rb +131 -0
- data/lib/rubycc/preprocess/constant_expression.rb +219 -0
- data/lib/rubycc/preprocess/glibc_version.rb +124 -0
- data/lib/rubycc/preprocess/pp_token.rb +62 -0
- data/lib/rubycc/preprocess/preprocessor.rb +2020 -0
- data/lib/rubycc/preprocess/scanner.rb +290 -0
- data/lib/rubycc/preprocess/token_converter.rb +157 -0
- data/lib/rubycc/rmake/cli.rb +143 -0
- data/lib/rubycc/rmake/errors.rb +71 -0
- data/lib/rubycc/rmake/executor.rb +818 -0
- data/lib/rubycc/rmake/expander.rb +251 -0
- data/lib/rubycc/rmake/makefile.rb +352 -0
- data/lib/rubycc/rmake/model.rb +137 -0
- data/lib/rubycc/rmake/parser.rb +226 -0
- data/lib/rubycc/rmake/rmake.rb +15 -0
- data/lib/rubycc/type.rb +1236 -0
- data/lib/rubycc/version.rb +5 -0
- data/lib/rubycc.rb +36 -0
- data/lib/rubygems_plugin.rb +102 -0
- 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
|